A <- 1:5
B <- 11:15
names(A) <- A
names(B) <- B
A
B
View(anscombe)
lm(y3~x3, data = anscombe)
##-- now some "magic" to do the 4 regressions in a loop:
ff <- y ~ x
mods <- setNames(as.list(1:4), paste0("lm", 1:4))
for(i in 1:4) {
ff[2:3] <- lapply(paste0(c("y","x"), i), as.name)
## or ff[[2]] <- as.name(paste0("y", i))
## ff[[3]] <- as.name(paste0("x", i))
mods[[i]] <- lmi <- lm(ff, data = anscombe)
print(anova(lmi))
}
## See how close they are (numerically!)
sapply(mods, coef)
lapply(mods, function(fm) coef(summary(fm)))
# Aggregate function
#Splits the data into subsets, computes summary statistics for each, and returns the result in a convenient form
testDF <- data.frame(v1 = c(1,3,5,7,8,3,5,NA,4,5,7,9),
v2 = c(11,33,55,77,88,33,55,NA,44,55,77,99) )
by1 <- c("red", "blue", 1, 2, NA, "big", 1, 2, "red", 1, NA, 12)
by2 <- c("wet", "dry", 99, 95, NA, "damp", 95, 99, "red", 99, NA, NA)
aggregate(x = testDF, by = list(by1, by2), FUN = "mean")
## Now, do what you should have done in the first place: PLOTS
op <- par(mfrow = c(2, 2), mar = 0.1+c(4,4,1,1), oma = c(0, 0, 2, 0))
for(i in 1:4) {
ff[2:3] <- lapply(paste0(c("y","x"), i), as.name)
plot(ff, data = anscombe, col = "red", pch = 21, bg = "orange", cex = 1.2,
xlim = c(3, 19), ylim = c(3, 13))
abline(mods[[i]], col = "blue")
}
mtext("Anscombe's 4 Regression data sets", outer = TRUE, cex = 1.5)
par(op)
x = c(TRUE, FALSE, FALSE)
typeof(x) #logical (vector)
mode(x)
storage.mode(x)
y = 1:10
typeof(y)
mode(y)
storage.mode(y)
dim(y)
length(y)
z= list(1, TRUE, 'safs') #trying to get a list
typeof(z)
class(z)
z[3]
length(z)
dim(z)
quote(x+y)
as.list(quote(x + y))
1e3L #create constant
1L
cat(1+2)
'+'(1, 2)
x <- options()
x$prompt
match(NA, NaN)
match(NA, NA)
match(NaN, NaN)
x = array(1:8, c(2,4))
x
i=2
j=3
x[i]
x[i, j]
x[[i]]
x[[i, j]]
rownames(x)=c("a","b")
x
x = as.data.frame(x)
x
x["a",]
x[]
i <- matrix(1:4, 2, byrow = TRUE)
i
i[2,]
i[2, ,drop=FALSE] # keeping dimension 1 * n when selection a row
dim(i[2,])
dim(i[2, ,drop=FALSE])
# https://cran.r-project.org/doc/manuals/R-intro.pdf
help.start()
x <- rnorm(10000)
y <- rnorm(x)
plot(x, y)
hist(y)
ls()
rm(list=ls())
ls()
x <- 1:20
w <- 1 + sqrt(x)/2
dummy <- data.frame(x=x, y= x + rnorm(x)*w)
dummy
# 4 Ordered and unordered factors
state <- c("tas", "sa", "qld", "nsw", "nsw", "nt", "wa", "wa",
"qld", "vic", "nsw", "vic", "qld", "qld", "sa", "tas",
"sa", "nt", "wa", "vic", "qld", "nsw", "nsw", "wa",
"sa", "act", "nsw", "vic", "vic", "act")
statef <- factor(state)
statef
levels(statef)
incomes <- c(60, 49, 40, 61, 64, 60, 59, 54, 62, 69, 70, 42, 56,
61, 61, 61, 58, 51, 48, 65, 49, 49, 41, 48, 52, 46,
59, 46, 58, 43)
incmeans <- tapply(incomes, statef, mean)
incmeans
# Arrays
a <- array(1:30, dim=c(2, 5,3))
a
x <- array(1:20, dim=c(4,5)) # Generate a 4 by 5 array.
x
i <- array(c(1:3,3:1), dim=c(3,2))
i
x[i] #get the 3 elements shown by i: (3, 1), (2, 2) and (1, 3)
help("<-")
# Back to http://zoonek2.free.fr/UNIX/48_R/02.html
x <- rnorm(10)
x
sort(x)
order(x)
x[order(x)]
x <- sample(1:5, 10, replace=T)
x
x[order(x)]
unique(x)
seq(0,10, length=11) == seq(0,10, by=1)
# Rep
rep(0, 10)
rep(1:5,3)
rep(1:5, each=3)
rep(1:5,2,each=3)
# Factor
x <- factor( sample(c("Yes", "No", "Perhaps"), 5, replace=T) )
x
# specify levels
l <- c("Yes", "No", "Perhaps")
x <- factor( sample(l, 5, replace=T), levels=l )
x
table(x)
# gl: Generate Factor Levels
gl(1,4)
gl(2,4)
gl(2,1,8)
gl(2,1,8, labels=c(T,F))
x <- gl(2,4)
x
y <- gl(2,1,8)
y
interaction(x,y)
data.frame(x,y, int=interaction(x,y))
# Cartesian product (toutes les possibilites)
x <- c("A", "B", "C")
y <- 1:2
z <- c("a", "b")
expand.grid(x,y,z)
x <- factor(c(3,4,5,1))
as.numeric( levels(x))
as.numeric( levels(x)[ x ] ) # proper way to convert from factor to numeric
# Data Frames
n <- 10
df <- data.frame( x=rnorm(n), y=sample(c(T,F),n,replace=T) )
str(df)
summary(df)
names(df);cat(rownames(df))
# Merge
merge(x, y) # INNER JOIN
merge(x, y, all.x = TRUE) # LEFT JOIN
merge(x, y, all.y = TRUE) # RIGHT JOIN
merge(x, y, all = TRUE) # OUTER JOIN
merge(a, b, by=c("y", "z")) # specify what to merge on
Error in fix.by(by.x, x) : 'by' must specify uniquely valid columns
# Regression
data(cars)
#View(cars)
# Regression
lm.fit=lm( dist ~ speed, data=cars, na.action = na.exclude)
lm.fit
# Polynomial regression
lm.fit3 = lm( dist ~ poly(speed,3), data=cars)
#plot(lm.fit)
plot(cars$speed, cars$dist)
abline(lm.fit)
library(rpart.plot)
Loading required package: rpart
fit <- rpart(Survived ~ Pclass + Sex + Age + SibSp + Parch + Fare + Embarked,
data=train,
method="class",
control=rpart.control(minsplit=2, cp=0))
Error in is.data.frame(data) : object 'train' not found
# Lists
h <- list()
h[["foo"]] <- 1
h[["bar"]] <- c("a", "b", "c")
h["bar"] == h[["bar"]] #h["bar"] is a list containing the vector
# Delete
h[["bar"]] <- NULL
m <- matrix( c(1,2,3,4), nrow=2 )
m
solve(m)
x <- matrix( c(6,7), nrow=2 )
solve(m, x)
?solve
n <- 1000
x1 <- factor( sample(1:3, n, replace=T), levels=1:3 )
x2 <- factor( sample(LETTERS[1:5], n, replace=T), levels=LETTERS[1:5] )
x3 <- factor( sample(c(F,T),n,replace=T), levels=c(F,T) )
d <- data.frame(x1,x2,x3)
r <- table(d)
r
ftable(d) #easier reading
# contingency table into a data.frame
n <- 100
k <- 10
x <- factor( sample(LETTERS[1:k], n, replace=T), levels=LETTERS[1:k] )
x
d <- table(x)
x2 = factor( rep(names(d),d), levels=names(d) )
x2
# apply
options(digits=4)
df <- data.frame(x=rnorm(20),y=rnorm(20),z=rnorm(20))
apply(df,2,mean)
rownames(df) <- LETTERS[1:20]
apply(df, 1, mean)
gl(2,10,20)
tapply(1:20, gl(2,10,20), sum) # tapply: 2nd argument used for grouping
by(1:20, gl(2,10,20), sum)
x <- list(a=rnorm(10), b=runif(100), c=rgamma(50,1))
sapply(x,sd) # sapply: apply FUN on each element of vector
lapply(x,sd) # lapply: same but returns list
# Exercise: Let x be a boolean vector. Count the number of sequences ("runs") of zeros (for instance, in 00101001010110, there are 6 runs: 00 0 00 0 0 0). Count the number of sequences of 1. Counth the total number of sequences. Same question for a factor with more tham two levels.
n <- 50
x <- sample(0:1, n, replace=T, p=c(.2,.8))
x
diff(x, lag=1)
#Let r be the return of a financial asset. The clustered return is the accumulated return for a sequence of returns of the same sign. The trend number is the number of steps in such a sequence. The average return is their ratio. Compute these quantities.
data(EuStockMarkets)
x <- EuStockMarkets
# We aren't interested in the spot prices, but in the returns
# return[i] = ( price[i] - price[i-1] ) / price[i-1]
y <- apply(x, 2, function (x) { diff(x)/x[-length(x)] })
# We normalize the data
z <- apply(y, 2, function (x) { (x-mean(x))/sd(x) })
# A single time series
r <- z[,1]
# The runs
f <- factor(cumsum(abs(diff(sign(r))))/2)
r <- r[-1]
accumulated.return <- tapply(r, f, sum)
trend.number <- table(f)
boxplot(abs(accumulated.return) ~ trend.number, col='pink',
main="Accumulated return")
# Strings
print("Hello\n")
cat("Hello\n") #use cat
paste("Hello", "World", "!", sep="") #concatenate
paste("Hello ", " World", "!", sep="")
x <- 5
paste("x=", x)
cat("x=", x, "\n", sep="\n")
s <- c("Hello", " ", "World", "!")
paste(s)
paste(s, sep="")
paste(s, collapse="")
paste(1:3, "Hello World!", sep=":")
nchar("Hello World!")
s <- "Hello World"
substring(s, 4, 6)
s <- "foo-->bar-->baz"
strsplit(s, "-->")
# Regex
s <- "foo, bar, baz"
strsplit(s, ", *")
s <- apply(matrix(LETTERS[1:24], nr=4), 2, paste, collapse="")
s
grep("O", s)
grep("O", s, value=T)
regexpr("o", "Hello")
regexpr("o", c("Hello", "World!"))
s <- "foo bar baz"
gsub(" ", "", s) # Remove all the spaces
gsub(" +", " ", s) # Remove multiple spaces and replace them by single spaces
#The "sub" is similar to "gsub" but only replaces the first occurrence.
s <- "foo bar baz"
sub(" ", "", s)
# Dates
as.Date("2005-05-15") #ISO 8601
as.Date("15/05/2005", format="%d/%m/%Y")
as.Date("15/05/05", format="%d/%m/%y")
cat("\n")
as.Date("01/02/03", format="%y/%m/%d")
as.Date("01/02/03", format="%y/%d/%m")
as.Date("01/02/03", format="%y/%m/%d") - as.Date("01/02/03", format="%y/%d/%m")
Sys.Date()
format(Sys.Date(), format="%A, %d %B %Y")
seq(as.Date("2005-01-01"), as.Date("2005-07-01"), by="month")
seq(as.Date("2005-01-01"), as.Date("2005-07-01"), by=31)
methods(class="Date")
as.POSIXlt("2005-05-15 21:45:17")
as.POSIXct(Sys.Date())
# Reading from dataframes
# option 1
#d <- read.table("foo.txt")
#d$Date <- as.Date( as.character( d$Date ) )
# option 2
#read.table("foo.txt", colClasses=c("Date", "character", rep(10, "numeric")))
options(warn=1)
methods(plot)
# Import
# d <- read.table("foo.txt", header=T, sep=",")
# d <- read.csv("txt.csv")
# d <- read.csv2("txt.csv") # semicolon-separated file, with a
# # comma instead of the decimal point.
# d <- read.delim("foo.txt") # Tab-delimited file
# d <- read.fwf("txt.fwf") # Fixed width fields
# Excel: this may be trickier: the missing values often appear as "#N/A!" and are mistaken for the start of a comment... You can try
# d <- read.table("foo.csv", header = TRUE, sep = ",",
# na.strings = c("#N/A!", "NA", "@NA"),
# quote = '"',
# comment.char = "")
#If your file only contains number, or only strings, it is wiser to store it in a matrix, not a data.frame. This is what the "scan" function does.
# A numeric matrix
# x <- scan("foo.txt", sep=",") # Gives a numeric vector
# n <- scan("foo.txt", sep=",", nlines=1)
# x <- matrix(x, nc=n)
# A vector of strings
#x <- scan("foo.txt", what=character(0))
# Back tohttps://cran.r-project.org/doc/manuals/R-intro.pdf - Regression
# fm05 <- lm(y ~ x1 + x2 + x3 + x4 + x5, data = production)
# fm6 <- update(fm05, . ~ . + x6)
# smf6 <- update(fm6, sqrt(.) ~ .)
# Spine (from help)
require(graphics)
op <- par(mfrow = c(2,1), mgp = c(2,.8,0), mar = 0.1+c(3,3,3,1))
n <- 9
x <- 1:n
y <- rnorm(n)
plot(x, y, main = paste("spline[fun](.) through", n, "points"))
lines(spline(x, y))
lines(spline(x, y, n = 210), col = 2)
# NA handling - http://thomasleeper.com/Rcourse/Tutorials/NA.html
g1 <- c(1, 2, NA, NA, NA, 6, 7)
g2 <- na.omit(g1)
g2
attributes(g2)$na.action
sum(g1)
sum(g1, na.rm = TRUE)
# Cor -> can eliminate only pair-wise NAs ()
x <- c(1, 2, 3, NA, 5, 7, 9)
y <- c(3, 2, 4, 5, 1, 3, 4)
z <- c(NA, 2, 3, 5, 4, 3, 4)
m <- data.frame(x, y, z)
m
cor(m) # returns all NAs
x y z
x 1 NA NA
y NA 1 NA
z NA NA 1
cor(m, use = "complete.obs")
x y z
x 1.0000 0.34819 0.70957
y 0.3482 1.00000 0.04583
z 0.7096 0.04583 1.00000
cor(m, use = "pairwise.complete.obs")
x y z
x 1.0000 0.2498 0.7096
y 0.2498 1.0000 0.4534
z 0.7096 0.4534 1.0000
# Defaut for lm is also casewise deletion
lm <- lm(y ~ x + z, data = m)
summary(lm)
Call:
lm(formula = y ~ x + z, data = m)
Residuals:
2 3 5 6 7
-0.632 1.711 -1.237 -0.447 0.605
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 3.316 3.399 0.98 0.43
x 0.289 0.408 0.71 0.55
z -0.632 1.396 -0.45 0.70
Residual standard error: 1.65 on 2 degrees of freedom
(2 observations deleted due to missingness)
Multiple R-squared: 0.203, Adjusted R-squared: -0.594
F-statistic: 0.254 on 2 and 2 DF, p-value: 0.797
# Checking length of data used for regression
length(m$y)
[1] 7
length(lm$fitted)
[1] 5
# checking where missing data is
is.na(m) # only useful for small examples
x y z
[1,] FALSE FALSE TRUE
[2,] FALSE FALSE FALSE
[3,] FALSE FALSE FALSE
[4,] TRUE FALSE FALSE
[5,] FALSE FALSE FALSE
[6,] FALSE FALSE FALSE
[7,] FALSE FALSE FALSE
# image
image(is.na(m), main = "Missing Values", xlab = "Observation", ylab = "Variable",
xaxt = "n", yaxt = "n", bty = "n")
axis(1, seq(0, 1, length.out = nrow(m)), 1:nrow(m), col = "white")
axis(2, c(0, 0.5, 1), names(m), col = "white", las = 2)

# to remove casewise:
m2 <- na.omit(m) # use new variable to keep original dataframe
m
m2
# Mean and Random imputation
x2 <- x
x2[is.na(x2)] <- mean(x2, na.rm = TRUE)
x
[1] 1 2 3 NA 5 7 9
x2
[1] 1.0 2.0 3.0 4.5 5.0 7.0 9.0
# Random imputation - conserve mean and variance. Sample rest of the values to fill NAs
x3 <- x
x3[is.na(x3)] <- sample(x3[!is.na(x3)], sum(is.na(x3)), TRUE)
x
[1] 1 2 3 NA 5 7 9
x3
[1] 1 2 3 5 5 7 9
# Saving R data http://thomasleeper.com/Rcourse/Tutorials/savingdata.html
set.seed(1)
mydf <- data.frame(x = rnorm(10), y = rnorm(10), z = rnorm(10))
save(mydf, file = "saveddf.RData")
unlink("saveddf.RData")
# dput to have a readable format (e.g. for stack overflow)
dput(mydf)
structure(list(x = c(-0.626453810742332, 0.183643324222082, -0.835628612410047,
1.59528080213779, 0.329507771815361, -0.820468384118015, 0.487429052428485,
0.738324705129217, 0.575781351653492, -0.305388387156356), y = c(1.51178116845085,
0.389843236411431, -0.621240580541804, -2.2146998871775, 1.12493091814311,
-0.0449336090152309, -0.0161902630989461, 0.943836210685299,
0.821221195098089, 0.593901321217509), z = c(0.918977371608218,
0.782136300731067, 0.0745649833651906, -1.98935169586337, 0.61982574789471,
-0.0561287395290008, -0.155795506705329, -1.47075238389927, -0.47815005510862,
0.417941560199702)), .Names = c("x", "y", "z"), row.names = c(NA,
-10L), class = "data.frame")
dput(mydf, "saveddf.txt")
mydf2 <- dget("saveddf.txt")
mydf2
mydf==mydf2
x y z
[1,] FALSE FALSE FALSE
[2,] FALSE FALSE FALSE
[3,] FALSE FALSE TRUE
[4,] FALSE TRUE FALSE
[5,] FALSE FALSE FALSE
[6,] FALSE FALSE FALSE
[7,] FALSE FALSE FALSE
[8,] FALSE FALSE FALSE
[9,] FALSE FALSE FALSE
[10,] TRUE FALSE FALSE
unlink("saveddf.text")
# csv
write.csv(mydf, file = "saveddf.csv")
unlink("savedf.csv")
# Dataframe rearrangement
set.seed(50)
mydf <- data.frame(a = rep(1:2, each = 10), b = rep(1:4, times = 5), c = rnorm(20),
d = rnorm(20), e = sample(1:20, 20, FALSE))
head(mydf)
# manual order change
head(mydf[, c("c", "d", "e", "a", "b")])
# mydf <- mydf[, c(3, 4, 5, 1, 2)]
# using order
order(mydf$e)
[1] 2 16 18 19 20 14 3 4 5 12 1 11 13 15 7 8 10 9 6 17
head(mydf[order(mydf$e), ])
# Subset
mydf[mydf$a == 1, ]
mydf[mydf$a == 1 & mydf$b > 2, ]
subset(mydf, a == 1 & b > 2)
subset(mydf, select = c("a", "b"))
# Splitting
split(mydf, mydf$a)
$`1`
$`2`
NA
split(mydf, list(mydf$a, mydf$b))
$`1.1`
$`2.1`
$`1.2`
$`2.2`
$`1.3`
$`2.3`
$`1.4`
$`2.4`
lapply(split(mydf, mydf$a), summary)
$`1`
a b c d e
Min. :1 Min. :1.00 Min. :-1.728 Min. :-1.590 Min. : 1.00
1st Qu.:1 1st Qu.:1.25 1st Qu.:-0.779 1st Qu.:-0.359 1st Qu.: 8.25
Median :1 Median :2.00 Median :-0.122 Median : 0.193 Median :13.00
Mean :1 Mean :2.30 Mean :-0.244 Mean : 0.299 Mean :12.10
3rd Qu.:1 3rd Qu.:3.00 3rd Qu.: 0.483 3rd Qu.: 0.568 3rd Qu.:16.75
Max. :1 Max. :4.00 Max. : 0.976 Max. : 2.668 Max. :19.00
$`2`
a b c d e
Min. :2 Min. :1.00 Min. :-1.166 Min. :-1.1304 Min. : 2.00
1st Qu.:2 1st Qu.:2.00 1st Qu.:-0.488 1st Qu.:-0.6338 1st Qu.: 4.25
Median :2 Median :3.00 Median :-0.343 Median : 0.2961 Median : 8.00
Mean :2 Mean :2.70 Mean :-0.268 Mean : 0.0793 Mean : 8.90
3rd Qu.:2 3rd Qu.:3.75 3rd Qu.: 0.108 3rd Qu.: 0.4137 3rd Qu.:12.75
Max. :2 Max. :4.00 Max. : 0.555 Max. : 1.8397 Max. :20.00
lapply(split(mydf, mydf$a), summary)
$`1`
a b c d e
Min. :1 Min. :1.00 Min. :-1.728 Min. :-1.590 Min. : 1.00
1st Qu.:1 1st Qu.:1.25 1st Qu.:-0.779 1st Qu.:-0.359 1st Qu.: 8.25
Median :1 Median :2.00 Median :-0.122 Median : 0.193 Median :13.00
Mean :1 Mean :2.30 Mean :-0.244 Mean : 0.299 Mean :12.10
3rd Qu.:1 3rd Qu.:3.00 3rd Qu.: 0.483 3rd Qu.: 0.568 3rd Qu.:16.75
Max. :1 Max. :4.00 Max. : 0.976 Max. : 2.668 Max. :19.00
$`2`
a b c d e
Min. :2 Min. :1.00 Min. :-1.166 Min. :-1.1304 Min. : 2.00
1st Qu.:2 1st Qu.:2.00 1st Qu.:-0.488 1st Qu.:-0.6338 1st Qu.: 4.25
Median :2 Median :3.00 Median :-0.343 Median : 0.2961 Median : 8.00
Mean :2 Mean :2.70 Mean :-0.268 Mean : 0.0793 Mean : 8.90
3rd Qu.:2 3rd Qu.:3.75 3rd Qu.: 0.108 3rd Qu.: 0.4137 3rd Qu.:12.75
Max. :2 Max. :4.00 Max. : 0.555 Max. : 1.8397 Max. :20.00
# sampling
s <- sample(1:nrow(mydf), 5, F) #no replacement
s
[1] 18 1 12 15 6
mydf[s,]
# test set
mydf[-s, ]
# Option 2
s2 <- rbinom(nrow(mydf), 1, 0.2)
s2
[1] 1 0 0 1 0 0 0 0 1 0 0 0 0 0 0 0 0 0 1 0
mydf[s2,]
mydf[-s2,]
library(car)
Attaching package: ‘car’
The following object is masked from ‘package:dplyr’:
recode
b <- 1:20
#h <- recode(b, "1:5=1: 6:10=2; else=NA") # incredibly this creates an error
e <- recode(b, "1:5=1; 6:10=2; else=NA")
e
[1] 1 1 1 1 1 2 2 2 2 2 NA NA NA NA NA NA NA NA NA NA
f <- recode(b, "lo:5=1; 6:10=2; 11:15=3; 16:hi=4; else=NA")
f
[1] 1 1 1 1 1 2 2 2 2 2 3 3 3 3 3 4 4 4 4 4
e <- recode(h, "NA=99")
e
[1] 1 2 3 4 5 99 99 99 99 99 99 99 99 99 99 99 99 99 99 99
# Reconding on multiple variables
i <- expand.grid(1:4, 1:2)
i
interaction(i$Var1, i$Var2) # creates all possible combinations
[1] 1.1 2.1 3.1 4.1 1.2 2.2 3.2 4.2
Levels: 1.1 2.1 3.1 4.1 1.2 2.2 3.2 4.2
set.seed(1)
n <- 30
mydf <- data.frame(x1 = rbinom(n, 1, 0.5), x2 = rbinom(n, 1, 0.1), x3 = rbinom(n,
1, 0.5), x4 = rbinom(n, 1, 0.8), x5 = 1, x6 = sample(c(0, 1, NA), n, TRUE))
str(mydf)
'data.frame': 30 obs. of 6 variables:
$ x1: int 0 0 1 1 0 1 1 1 1 0 ...
$ x2: int 0 0 0 0 0 0 0 0 0 0 ...
$ x3: int 1 0 0 0 1 0 0 1 0 1 ...
$ x4: int 1 1 1 0 1 1 1 1 0 1 ...
$ x5: num 1 1 1 1 1 1 1 1 1 1 ...
$ x6: num NA 1 1 0 NA 1 1 0 0 1 ...
mydf$x1 + mydf$x2 - mydf$x3 # vector operations
[1] -1 0 1 1 -1 1 1 0 1 -1 0 -1 1 0 1 -1 0 1 -1 0 1 -1 1 0 -1 0 -1 0 1
[30] 0
with(mydf, x1+x2-x3)
[1] -1 0 1 1 -1 1 1 0 1 -1 0 -1 1 0 1 -1 0 1 -1 0 1 -1 1 0 -1 0 -1 0 1
[30] 0
rowSums(mydf)
[1] NA 3 4 2 NA 4 4 4 2 4 3 3 3 2 NA 4 5 4 NA 5 NA 4 3 2 NA 3 3 NA 3
[30] NA
rowSums(mydf, na.rm = T)
[1] 3 3 4 2 3 4 4 4 2 4 3 3 3 2 3 4 5 4 2 5 2 4 3 2 3 3 3 2 3 2
data.frame(1:n, rowSums(mydf, na.rm = T))
rowMeans(mydf)
[1] NA 0.5000 0.6667 0.3333 NA 0.6667 0.6667 0.6667 0.3333 0.6667 0.5000 0.5000
[13] 0.5000 0.3333 NA 0.6667 0.8333 0.6667 NA 0.8333 NA 0.6667 0.5000 0.3333
[25] NA 0.5000 0.5000 NA 0.5000 NA
apply(mydf, 1, var) # 2nd argument: 1 for rows, 2 for columns, c(1, 2) rows & columns.
[1] NA 0.3000 0.2667 0.2667 NA 0.2667 0.2667 0.2667 0.2667 0.2667 0.3000 0.3000
[13] 0.3000 0.2667 NA 0.2667 0.1667 0.2667 NA 0.1667 NA 0.2667 0.3000 0.2667
[25] NA 0.3000 0.3000 NA 0.3000 NA
apply(mydf, 2, var)
x1 x2 x3 x4 x5 x6
0.2575 0.0000 0.2483 0.1437 0.0000 NA
sapply(mydf, var) # over list or vector
x1 x2 x3 x4 x5 x6
0.2575 0.0000 0.2483 0.1437 0.0000 NA
newvar
[1] 3 2 0 0 3 0 0 1 0 3 2 3 0 1 0 3 1 0 2 1 0 3 0 2 3 2 3 2 0 2
newvar[mydf$x1 == 1] <- with(mydf, x2 + x3)
number of items to replace is not a multiple of replacement length
# Matrices
set.seed(1)
a <- rnorm(100)
quantile(a, c(0.025, 0.975))
2.5% 97.5%
-1.671 1.797
quantile(a, seq(0, 1, by = 0.1))
0% 10% 20% 30% 40% 50% 60% 70% 80% 90% 100%
-2.2147 -1.0527 -0.6139 -0.3753 -0.0767 0.1139 0.3771 0.5812 0.7713 1.1811 2.4016
summary(as.logical(rbinom(1000, 1, 0.5)))
Mode FALSE TRUE
logical 492 508
summary(factor(a))
-2.2146998871775 -1.98935169586337 -1.80495862889104 -1.52356680042976
1 1 1 1
-1.47075238389927 -1.37705955682861 -1.27659220845804 -1.2536334002391
1 1 1 1
-1.22461261489836 -1.12936309608079 -1.04413462631653 -0.934097631644252
1 1 1 1
-0.835628612410047 -0.820468384118015 -0.743273208882405 -0.709946430921815
1 1 1 1
-0.70749515696212 -0.68875569454952 -0.626453810742332 -0.621240580541804
1 1 1 1
-0.612026393250771 -0.589520946188072 -0.573265414236886 -0.568668732818502
1 1 1 1
-0.54252003099165 -0.47815005510862 -0.473400636439312 -0.443291873218433
1 1 1 1
-0.41499456329968 -0.394289953710349 -0.367221476466509 -0.305388387156356
1 1 1 1
-0.304183923634301 -0.253361680136508 -0.164523596253587 -0.155795506705329
1 1 1 1
-0.135178615123832 -0.135054603880824 -0.112346212150228 -0.102787727342996
1 1 1 1
-0.0593133967111857 -0.0561287395290008 -0.0538050405829051 -0.0449336090152309
1 1 1 1
-0.0392400027331692 -0.0161902630989461 0.00110535163162413 0.0280021587806661
1 1 1 1
0.0743413241516641 0.0745649833651906 0.153253338211898 0.183643324222082
1 1 1 1
0.188792299514343 0.267098790772231 0.291446235517463 0.329507771815361
1 1 1 1
0.332950371213518 0.341119691424425 0.36458196213683 0.370018809916288
1 1 1 1
0.387671611559369 0.389843236411431 0.398105880367068 0.417941560199702
1 1 1 1
0.475509528899663 0.487429052428485 0.556663198673657 0.558486425565304
1 1 1 1
0.569719627442413 0.575781351653492 0.593901321217509 0.593946187628422
1 1 1 1
0.610726353489055 0.61982574789471 0.689739362450777 0.696963375404737
1 1 1 1
0.700213649514998 0.738324705129217 0.763175748457544 0.768532924515416
1 1 1 1
0.782136300731067 0.821221195098089 0.881107726454215 0.918977371608218
1 1 1 1
0.943836210685299 1.06309983727636 1.10002537198388 1.12493091814311
1 1 1 1
1.16040261569495 1.1780869965732 1.20786780598317 1.35867955152904
1 1 1 1
1.43302370170104 1.46555486156289 1.51178116845085 1.58683345454085
1 1 1 1
1.59528080213779 1.98039989850586 2.17261167036215 2.40161776050478
1 1 1 1
# Tables
set.seed(1)
a <- sample(1:5, 25, T)
a
[1] 2 2 3 5 2 5 5 4 4 1 2 1 4 2 4 3 4 5 2 4 5 2 4 1 2
table(a)
a
1 2 3 4 5
3 8 2 7 5
prop.table(table(a)) # to obtain percentages
a
1 2 3 4 5
0.12 0.32 0.08 0.28 0.20
prop.table(table(a)) *100
a
1 2 3 4 5
12 32 8 28 20
cbind(table(a), prop.table(table(a))*100)
[,1] [,2]
1 3 12
2 8 32
3 2 8
4 7 28
5 5 20
# multi-variate
b <- rep(c(1, 2), length = 25)
table(a, b)
b
a 1 2
1 0 3
2 5 3
3 1 1
4 5 2
5 2 3
c <- rep(c(3, 4, 5), length = 25)
table(a, b, c)
, , c = 3
b
a 1 2
1 0 1
2 3 1
3 0 1
4 1 0
5 1 1
, , c = 4
b
a 1 2
1 0 0
2 2 2
3 0 0
4 2 2
5 0 0
, , c = 5
b
a 1 2
1 0 2
2 0 0
3 1 0
4 2 0
5 1 2
ftable(a, c, c)
c 3 4 5
a c
1 3 1 0 0
4 0 0 0
5 0 0 2
2 3 4 0 0
4 0 4 0
5 0 0 0
3 3 1 0 0
4 0 0 0
5 0 0 1
4 3 1 0 0
4 0 4 0
5 0 0 2
5 3 2 0 0
4 0 0 0
5 0 0 3
xtabs(~a + b)
b
a 1 2
1 0 3
2 5 3
3 1 1
4 5 2
5 2 3
x <- table(a, b)
addmargins(x)
b
a 1 2 Sum
1 0 3 3
2 5 3 8
3 1 1 2
4 5 2 7
5 2 3 5
Sum 13 12 25
prop.table(table(a, b), 1) # proportions by rows
b
a 1 2
1 0.0000000 1.0000000
2 0.6250000 0.3750000
3 0.5000000 0.5000000
4 0.7142857 0.2857143
5 0.4000000 0.6000000
prop.table(table(a, b), 2)
b
a 1 2
1 0.00000000 0.25000000
2 0.38461538 0.25000000
3 0.07692308 0.08333333
4 0.38461538 0.16666667
5 0.15384615 0.25000000
# Correlations
set.seed(1)
n <- 1000
x1 <- rnorm(n, -1, 10)
x2 <- rnorm(n, 3, 2)
y <- 5 * x1 + x2 + rnorm(n, 1, 2)
cor(x1, x2)
[1] 0.006401211
cor.test(x1, x2)
Pearson's product-moment correlation
data: x1 and x2
t = 0.20223, df = 998, p-value = 0.8398
alternative hypothesis: true correlation is not equal to 0
95 percent confidence interval:
-0.05561394 0.06836716
sample estimates:
cor
0.006401211
cor(cbind(x1, x2, y))
x1 x2 y
x1 1.000000000 0.006401211 0.99837564
x2 0.006401211 1.000000000 0.04731214
y 0.998375645 0.047312138 1.00000000
a <- rnorm(n)
b <- a^2 + rnorm(n)
plot(b~a)

plot(b ~ a, col = "gray")
curve((x), col = "red", add = TRUE)
curve((x^2), col = "blue", add = TRUE)

cor(a, b)
[1] -0.01712362
cor(a^2, b)
[1] 0.8430192
plot(b~I(a^2), col = "orange")
abline(lm(b~I(a^2)), col = "red")

layout(matrix(1:2, nrow = 1))
plot(b ~ a, col = "gray")
curve((x^2), col = "blue", add = TRUE)
plot(b ~ I(a^2), col = "gray")
curve((x), col = "blue", add = TRUE)

# Rounding
height <- c(167, 164, 172, 158, 181, 179)
mean(height)
[1] 170.1667
signif(mean(height), 4)
[1] 170.2
round(mean(height), 1)
[1] 170.2
round(mean(height), -2)
[1] 200
options(digits = 5)
sd(height)
[1] 8.8863
options(digits = 2)
sd(height)
[1] 8.9
options(scipen = -10)
10000000
[1] 1e+07
# sprintf
sprintf("%.3f", pi)
[1] "3.142"
sprintf("%05.1f", pi)
[1] "003.1"
# Plots as data summary
set.seed(1)
a <- rnorm(30)
hist(a, col = "gray20", border = "lightgray")

density(a)
Call:
density.default(x = a)
Data: a (30 obs.); Bandwidth 'bw' = 0.3891
x y
Min. :-3.4e+00 Min. :0.0e+00
1st Qu.:-1.8e+00 1st Qu.:3.0e-02
Median :-3.0e-01 Median :9.0e-02
Mean :-3.0e-01 Mean :1.6e-01
3rd Qu.: 1.2e+00 3rd Qu.:2.9e-01
Max. : 2.8e+00 Max. :4.6e-01
plot(density(a))

hist(a, freq = FALSE, col = "gray20", border = "lightgray")
lines(density(a), col = "red", lwd = 2)

b <- c(3, 4.5, 5, 8, 3, 6)
barplot(b, names.arg = letters[1:length(b)], horiz = F)

d <- rbind(c(2, 4, 1), c(6, 1, 3))
d
[,1] [,2] [,3]
[1,] 2e+00 4e+00 1e+00
[2,] 6e+00 1e+00 3e+00
barplot(d, names.arg = letters[1:3])

barplot(d, beside = T)

layout(matrix(1:2, nrow = 1))
barplot(b, names.arg = letters[1:6], horiz = TRUE, las = 2)
dotchart(b, labels = letters[1:6], xlim = c(0, 8))

boxplot(a)

e <- rnorm(100, 1, 1)
f <- rnorm(100, 2, 4)
boxplot(e, f)

g1 <- c(e, f)
g2 <- rep(c(1, 2), each = 100)
boxplot(g1 ~ g2)

# Scatterplot
x1 <- rnorm(1000)
x2 <- rnorm(1000)
x3 <- x1 + x2
x4 <- x1 + x3
plot(x1, x2)

plot(x2~x1)

layout(matrix(1:3, nrow = 1))
plot(x1, x2)
plot(x1, x3)
plot(x1, x4)

pairs(~x1 + x2 + x3 + x4)

colors()[1:10]
[1] "white" "aliceblue" "antiquewhite" "antiquewhite1" "antiquewhite2"
[6] "antiquewhite3" "antiquewhite4" "aquamarine" "aquamarine1" "aquamarine2"
length(colors())
[1] 657
colors()[600]
[1] "slategray1"
set.seed(100)
z <- sample(1:4, 100, TRUE)
x <- rnorm(100)
y <- rnorm(100)
plot(x, y, pch = 15, col = c("red", "blue"))

c("red", "blue", "green", "orange")[z]
[1] "blue" "blue" "green" "red" "blue" "blue" "orange" "blue" "green"
[10] "red" "green" "orange" "blue" "blue" "orange" "green" "red" "blue"
[19] "blue" "green" "green" "green" "green" "green" "blue" "red" "orange"
[28] "orange" "green" "blue" "blue" "orange" "blue" "orange" "green" "orange"
[37] "red" "green" "orange" "red" "blue" "orange" "orange" "orange" "green"
[46] "blue" "orange" "orange" "red" "blue" "blue" "red" "red" "blue"
[55] "green" "blue" "red" "red" "green" "red" "blue" "green" "orange"
[64] "green" "blue" "blue" "blue" "blue" "red" "green" "blue" "blue"
[73] "green" "orange" "green" "green" "orange" "orange" "orange" "red" "blue"
[82] "green" "orange" "orange" "red" "green" "green" "red" "blue" "green"
[91] "orange" "red" "blue" "blue" "orange" "blue" "green" "red" "red"
[100] "orange"
plot(x, y, pch = 15, col = c("red", "blue", "green", "orange")[z]) #indexing colors on z groups

# Analysis of variance (ANOVA)
set.seed(100)
tr <- rep(1:4, each = 30)
y <- numeric(length = 120)
y[tr == 1] <- rnorm(30, 5, 1)
y[tr == 2] <- rnorm(30, 4, 2)
y[tr == 3] <- rnorm(30, 4, 5)
y[tr == 4] <- rnorm(30, 1, 2)
aov(y~tr)
Call:
aov(formula = y ~ tr)
Terms:
tr Residuals
Sum of Squares 2.5e+02 1.2e+03
Deg. of Freedom 1.0e+00 1.2e+02
Residual standard error: 3.2e+00
Estimated effects may be unbalanced
summary(aov(y ~ factor(tr)))
Df Sum Sq Mean Sq F value Pr(>F)
factor(tr) 3.00e+00 2.97e+02 9.9e+01 9.98e+00 6.6e-06 ***
Residuals 1.16e+02 1.15e+03 9.9e+00
---
Signif. codes: 0e+00 ‘***’ 1e-03 ‘**’ 1e-02 ‘*’ 5e-02 ‘.’ 1e-01 ‘ ’ 1e+00
oneway.test(y ~ tr)
One-way analysis of means (not assuming equal variances)
data: y and tr
F = 4e+01, num df = 3e+00, denom df = 5e+01, p-value = 1e-13
oneway.test(y ~ factor(tr), var.equal = TRUE)
One-way analysis of means
data: y and factor(tr)
F = 1e+01, num df = 3e+00, denom df = 1e+02, p-value = 7e-06
by(y, tr, FUN = mean)
tr: 1
[1] 5e+00
------------------------------------------------------------------------------------
tr: 2
[1] 4.2e+00
------------------------------------------------------------------------------------
tr: 3
[1] 3.8e+00
------------------------------------------------------------------------------------
tr: 4
[1] 8.5e-01
tapply(y, tr, FUN = mean) # same thing
1 2 3 4
5.0e+00 4.2e+00 3.8e+00 8.5e-01
# Distributions
options(scipen = F)
options(digits = 5)
dnorm(0) # density
[1] 0.39894
dnorm(0, mean=-1)
[1] 0.24197
pnorm(0) # cumulative
[1] 0.5
pnorm(1.65) # 95% normal confidence interval
[1] 0.95053
pnorm(1.96)
[1] 0.975
pnorm(1.96) - pnorm(-1.96)
[1] 0.95
qnorm(c(0.025, 0.975)) # quantile
[1] -1.96 1.96
pnorm(qnorm(c(0.025, 0.975)))
[1] 0.025 0.975
# other distribution
dbinom(0, 1, 0.5)
[1] 0.5
pbinom(0, 1, 0.5)
[1] 0.5
qbinom(.95, 1, 0.5)
[1] 1
# Formulae
myformula <- ~x
class(myformula)
[1] "formula"
# interactions
y ~ x1 * x2
y ~ x1 * x2
# As strings
("y ~ x") == (y ~ x)
[1] TRUE
as.formula("y~x")
y ~ x
as.character(y ~ x)
[1] "~" "y" "x"
terms(y ~ x1 + x2)
y ~ x1 + x2
attr(,"variables")
list(y, x1, x2)
attr(,"factors")
x1 x2
y 0 0
x1 1 0
x2 0 1
attr(,"term.labels")
[1] "x1" "x2"
attr(,"order")
[1] 1 1
attr(,"intercept")
[1] 1
attr(,"response")
[1] 1
attr(,".Environment")
<environment: R_GlobalEnv>
update(y ~ x, ~. + x2)
y ~ x + x2
update(y ~ x, z ~ .)
z ~ x
# Bivariate Regression
set.seed(1)
bin <- rbinom(1000, 1, 0.5)
out <- 2 * bin + rnorm(1000)
by(out, bin, mean)
bin: 0
[1] -0.015881
---------------------------------------------------------------------
bin: 1
[1] 1.9662
t.test(out ~ bin)
Welch Two Sample t-test
data: out by bin
t = -30.3, df = 993, p-value <2e-16
alternative hypothesis: true difference in means is not equal to 0
95 percent confidence interval:
-2.1105 -1.8537
sample estimates:
mean in group 0 mean in group 1
-0.015881 1.966237
lm(out ~ bin)
Call:
lm(formula = out ~ bin)
Coefficients:
(Intercept) bin
-0.0159 1.9821
summary(lm(out~bin))$coef
Estimate Std. Error t value Pr(>|t|)
(Intercept) -0.015881 0.045341 -0.35027 7.2621e-01
bin 1.982119 0.065444 30.28724 1.9487e-143
plot(out ~ bin, col = "gray")
points(0:1, by(out, bin, mean), col = "blue", bg = "blue", pch = 23)
abline(coef(lm(out ~ bin)), col = "blue")

set.seed(1)
x <- runif(1000, 0, 10)
y <- 3 * x + rnorm(1000, 0, 5)
# glm plots
set.seed(1)
n <- 100
x <- runif(n, 0, 1)
y <- rbinom(n, 1, x)
plot(y ~ x, col = NULL, bg = rgb(0, 0, 0, 0.5), pch = 21) # bg: background color
abline(lm(y ~ x), lwd = 2) # lwd: line width (default: 1)

m1 <- glm(y ~ x, family = binomial(link = "logit"))
newdf <- data.frame(x = seq(0, 1, length.out = 100))
newdf
newdf$pout_logit <- predict(m1, newdf, se.fit = TRUE, type = "response")$fit
newdf[95:100,]
# build confidence intervals from standard error
newdf$pse_logit <- predict(m1, newdf, se.fit = TRUE, type = "response")$se.fit
newdf$plower_logit <- newdf$pout_logit - (1.96 * newdf$pse_logit) # 95% CI lower bound
newdf$pupper_logit <- newdf$pout_logit + (1.96 * newdf$pse_logit) # 95% CI upper bound
# qnorm(c(0.025, 0.975)) = (-1.96, +1.96)
newdf[1:10,c(1,2,3,5,4)]
Error: object 'newdf' not found
# now plot predicted values
with(newdf, plot(pout_logit ~ x, type = "l", lwd = 2))
with(newdf, lines(pupper_logit ~ x, type = "l", lty = 2))
with(newdf, lines(plower_logit ~ x, type = "l", lty = 2))


head(mpg)
#g <- ggplot(data = mpg, aes(x = displ, y = hwy))
ggplot(data = mpg, aes(x = displ, y = hwy)) + geom_point()

ggplot(data = mpg, aes(x = displ, y = hwy)) + geom_point() + geom_smooth(method = "lm") # with regression

ggplot(data = mpg, aes(x = displ, y = hwy)) + geom_point(aes(color = class))

ggplot(data = mpg, aes(x = displ, y = hwy)) + geom_point(aes(size = class))

ggplot(data = mpg, aes(x = displ, y = hwy)) + geom_point(aes(shape = class))

ggplot(data = mpg, aes(x = displ, y = hwy)) + geom_point(aes(alpha = class))

ggplot(data = mpg, aes(x = displ, y = hwy)) + geom_point() + facet_grid(. ~ cyl)

ggplot(data = mpg, aes(x = displ, y = hwy)) + geom_point() + facet_grid(drv ~ .)

ggplot(data = mpg, aes(x = displ, y = hwy)) + geom_point() + facet_grid(drv ~ cyl)

ggplot(data = mpg, aes(x = displ, y = hwy)) + geom_point() + facet_wrap( ~ class)

ggplot(data = mpg, aes(x = displ, y = hwy)) + geom_point() + geom_smooth()

ggplot(data = mpg, aes(x = displ, y = hwy)) + geom_point() + geom_smooth(se = FALSE) # Turn off confidence band

ggplot(data = mpg, aes(x = class, y = hwy)) + geom_boxplot() # scatterplot

ggplot(data = mpg, aes(x = reorder(class, hwy), y = hwy)) + geom_boxplot() # reorder (mean)

ggplot(data = mpg, aes(x = reorder(class, hwy, median), y = hwy)) + geom_boxplot() # reorder by median

# jitter
ggplot(data = mpg, aes(x = cty, y = hwy)) + geom_point()

ggplot(data = mpg, aes(x = cty, y = hwy)) + geom_point(position = "jitter")

ggplot(data = mpg, aes(x = cty, y = hwy)) + geom_jitter() # identical option

names(diamonds) # part of ggplot2 package
[1] "carat" "cut" "color" "clarity" "depth" "table" "price" "x" "y" "z"
ggplot(data = diamonds,aes(x =cut)) + geom_bar(aes(fill =cut)) # fill: color inside of bars

ggplot(data = diamonds, aes(x =cut)) + geom_bar(aes(color =cut)) # color: line around the bars

ggplot(data = diamonds, aes(x = color)) + geom_bar(aes(fill = cut), position = "dodge")

str(diamonds)
Classes ‘tbl_df’, ‘tbl’ and 'data.frame': 53940 obs. of 10 variables:
$ carat : num 0.23 0.21 0.23 0.29 0.31 0.24 0.24 0.26 0.22 0.23 ...
$ cut : Ord.factor w/ 5 levels "Fair"<"Good"<..: 5 4 2 4 2 3 3 3 1 3 ...
$ color : Ord.factor w/ 7 levels "D"<"E"<"F"<"G"<..: 2 2 2 6 7 7 6 5 2 5 ...
$ clarity: Ord.factor w/ 8 levels "I1"<"SI2"<"SI1"<..: 2 3 5 4 2 6 7 3 4 5 ...
$ depth : num 61.5 59.8 56.9 62.4 63.3 62.8 62.3 61.9 65.1 59.4 ...
$ table : num 55 61 65 58 58 57 57 55 61 61 ...
$ price : int 326 326 327 334 335 336 336 337 337 338 ...
$ x : num 3.95 3.89 4.05 4.2 4.34 3.94 3.95 4.07 3.87 4 ...
$ y : num 3.98 3.84 4.07 4.23 4.35 3.96 3.98 4.11 3.78 4.05 ...
$ z : num 2.43 2.31 2.31 2.63 2.75 2.48 2.47 2.53 2.49 2.39 ...
ggplot(data = diamonds, aes(x = carat)) + geom_histogram(binwidth = 1)

ggplot(data = diamonds, aes(x = carat)) + geom_histogram(binwidth = 0.01)

ggplot(data = diamonds, aes(x = carat)) + geom_histogram() #stat_bin: binwidth defaulted to range/30.

zoom <- coord_cartesian(xlim = c(55, 70))
ggplot(data = diamonds, aes(x = depth)) + geom_histogram(binwidth = 0.2) + zoom

ggplot(data = diamonds, aes(x = depth)) + geom_histogram(aes(fill = cut), binwidth = 0.1) + zoom

ggplot(data = diamonds, aes(x = depth)) + geom_density(aes(fill = cut)) + zoom

ggplot(data = diamonds, aes(x = depth)) + geom_density(aes(color = cut, fill = cut, alpha=0.1)) + zoom

ggplot(data = diamonds, aes(x = price)) +geom_histogram(aes(fill = cut), binwidth = 100)

ggplot(data = diamonds, aes(x = price)) + geom_density(aes(color= cut))

# Visualization of big data
ggplot(data = diamonds, aes(x = carat, y = price)) + geom_point(aes(color = cut)) # not helpful

ggplot(data = diamonds, aes(x = carat, y = price)) + geom_bin2d()

ggplot(data = diamonds, aes(x = carat, y = price)) + geom_density2d()

ggplot(data = diamonds, aes(x = carat, y = price)) + geom_point() + geom_density2d()

ggplot(data = diamonds, aes(x = carat, y = price)) + geom_smooth(aes(group = cut))
ggplot(data = diamonds, aes(x = carat, y = price)) + geom_smooth(aes(color = cut), method = "loess", se=F)

# ggsave("my-plot.pdf") # PDF more crisp
# ggsave("my-plot.png")
# ggsave("my-plot.pdf", width = 6, height = 6)
# ggsave("my-plot.png", width = 6, height = 6)
head(births, 3)
head(bnames, 3)
# Simple plot
Quentin <- bnames[bnames$name == "Letitia", ]
qplot(year, prop, data = Quentin, geom = "line")

# Interactions
michaels <- bnames[bnames$name == "Quentin" | bnames$name == "Alexis" | bnames$name == "Gina", ]
qplot(year, prop, data = michaels, geom = "line", color = interaction(sex, name))

# dplyr
library(dplyr)
bnames = tbl_df(bnames)
births = tbl_df(births)
class(bnames)
[1] "tbl_df" "tbl" "data.frame"
# filter
vivian = filter(bnames, name == "Vivian")
filter(bnames, sex == "girl" & (year == 1900 | year == 2000))
# Select columns
select(bnames, starts_with("sound"))
# Rename column
rename(iris, petal_length = Petal.Length)
# sort
arrange(bnames, desc(prop))[3,]
# year where Quentin was most popular
arrange(filter(bnames, name == "Quentin"), desc(prop))[1,]
# add columns
mutate(births, double = 2 * births)
# transmute to delete old columns
# Summarize
summarise(vivian,min = min(prop),mean = mean(prop), max = max(prop), number = n(), number_distinct = n_distinct(prop))
bnames2 = left_join(bnames, births, by = c("year","sex"))
bnames2 = mutate(bnames2, n = round(prop * births))
# Group
by_name = group_by(bnames2, name)
by_name
totals = summarise(by_name, total = sum(n))
totals
bnames2
name_sex = group_by(bnames2, name, year)
totals2 = summarise(name_sex, total = sum(n))
head(totals2)
arrange(bnames2, name)[1:3,]
ungroup(by_name)
# other example
year_sex = group_by(bnames2, year, sex)
ytotals = summarise(year_sex, births = sum(n))
ytotals
# ISLR
# Chap 6 Ridge regression / Lasso
# Ex 8
set.seed(1)
X = rnorm(100)
eps = rnorm(100)
beta0 = 3
beta1 = 2
beta2 = -3
beta3 = 0.3
Y = beta0 + beta1 * X + beta2 * X^2 + beta3 * X^3 + eps
# Use regsubsets to select best model having polynomial of XX of degree 10
library(leaps)
data.full = data.frame(y = Y, x = X)
mod.full = regsubsets(y ~ poly(x, 10, raw = T), data = data.full, nvmax = 10)
mod.summary = summary(mod.full)
# Find the model size for best cp, BIC and adjr2
which.min(mod.summary$cp)
[1] 3
which.min(mod.summary$bic)
[1] 3
which.max(mod.summary$adjr2)
[1] 3
# Plot cp, BIC and adjr2
plot(mod.summary$cp, xlab = "Subset Size", ylab = "Cp", pch = 20, type = "l")
points(3, mod.summary$cp[3], pch = 4, col = "red", lwd = 7)

plot(mod.summary$bic, xlab = "Subset Size", ylab = "BIC", pch = 20, type = "l")
points(3, mod.summary$bic[3], pch = 4, col = "red", lwd = 7)

plot(mod.summary$adjr2, xlab = "Subset Size", ylab = "Adjusted R2", pch = 20,
type = "l")
points(3, mod.summary$adjr2[3], pch = 4, col = "red", lwd = 7)

# Coefs found by regression (replaces x^3 by x^7)
coefficients(mod.full, id = 3)
(Intercept) poly(x, 10, raw = T)1 poly(x, 10, raw = T)2 poly(x, 10, raw = T)7
3.07627412 2.35623596 -3.16514887 0.01046843
# Now lasso
library(glmnet)
xmat = model.matrix(y ~ poly(x, 10, raw = T), data = data.full)[, -1] # remove intercept (first column)
mod.lasso = cv.glmnet(xmat, Y, alpha = 1) # cv.glmnet: cross validation to select best lambda
best.lambda = mod.lasso$lambda.min
best.lambda
[1] 0.03991416
plot(mod.lasso)

# Next fit the model on entire data using best lambda
best.model = glmnet(xmat, Y, alpha = 1)
predict(best.model, s = best.lambda, type = "coefficients")
11 x 1 sparse Matrix of class "dgCMatrix"
1
(Intercept) 3.0398151056
poly(x, 10, raw = T)1 2.2303371338
poly(x, 10, raw = T)2 -3.1033192679
poly(x, 10, raw = T)3 .
poly(x, 10, raw = T)4 .
poly(x, 10, raw = T)5 0.0498410763
poly(x, 10, raw = T)6 .
poly(x, 10, raw = T)7 0.0008068431
poly(x, 10, raw = T)8 .
poly(x, 10, raw = T)9 .
poly(x, 10, raw = T)10 .
#Lasso also picks X^5 over X^3. It also picks X^7 with negligible coefficient.
# ISLR
# Chap 7 Non-linear Modeling - Splines, GAM
library(ISLR)
attach(Wage)
The following objects are masked from Wage (pos = 3):
age, education, health, health_ins, jobclass, logwage, maritl, race, region, wage, year
fit=lm(wage~poly(age,4,raw=T),data=Wage) # raw=T to obtain coefficients of poly directly
coef(summary(fit)) # below we see small coef (and p-value) for order 3 & 4
Estimate Std. Error t value Pr(>|t|)
(Intercept) -1.841542e+02 6.004038e+01 -3.067172 0.0021802539
poly(age, 4, raw = T)1 2.124552e+01 5.886748e+00 3.609042 0.0003123618
poly(age, 4, raw = T)2 -5.638593e-01 2.061083e-01 -2.735743 0.0062606446
poly(age, 4, raw = T)3 6.810688e-03 3.065931e-03 2.221409 0.0263977518
poly(age, 4, raw = T)4 -3.203830e-05 1.641359e-05 -1.951938 0.0510386498
# Equivalent expressions
fit2a=lm(wage~age+I(age^2)+I(age^3)+I(age^4),data=Wage)
coef(fit2a)
(Intercept) age I(age^2) I(age^3) I(age^4)
-1.841542e+02 2.124552e+01 -5.638593e-01 6.810688e-03 -3.203830e-05
fit2b=lm(wage~cbind(age,age^2,age^3,age^4),data=Wage) # cbind in formulas <-> I() wrapper
coef(fit2b)
(Intercept) cbind(age, age^2, age^3, age^4)age cbind(age, age^2, age^3, age^4)
-1.841542e+02 2.124552e+01 -5.638593e-01
cbind(age, age^2, age^3, age^4) cbind(age, age^2, age^3, age^4)
6.810688e-03 -3.203830e-05
# Prediction
agelims=range(age)
age.grid=seq(from=agelims[1],to=agelims[2])
preds=predict(fit,newdata=list(age=age.grid),se=TRUE) # creates list of ages where we want prediction (see below)
se.bands=cbind(preds$fit+2*preds$se.fit,preds$fit-2*preds$se.fit)
par(mfrow=c(1,2),mar=c(4.5,4.5,1,1),oma=c(0,0,4,0)) # mar/ oma: margins of plot
plot(age,wage,xlim=agelims,cex=.5,col="darkgrey")
title("Degree-4 Polynomial",outer=T)
lines(age.grid,preds$fit,lwd=2,col="blue")
matlines(age.grid,se.bands,lwd=1,col="blue",lty=3)

age.grid=seq(from=agelims[1],to=agelims[2])
list(age=age.grid) # $age list
$age
[1] 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
[34] 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
predict(fit,list(age=c(70))) # test for 1 age - still need to create an $age list
1
106.9478
preds
$fit
1 2 3 4 5 6 7 8 9 10
51.93145 58.49674 64.57188 70.18273 75.35440 80.11122 84.47676 88.47380 92.12437 95.44973
11 12 13 14 15 16 17 18 19 20
98.47038 101.20601 103.67560 105.89731 107.88856 109.66599 111.24548 112.64214 113.87029 114.94351
21 22 23 24 25 26 27 28 29 30
115.87459 116.67557 117.35770 117.93148 118.40662 118.79210 119.09608 119.32598 119.48846 119.58939
31 32 33 34 35 36 37 38 39 40
119.63388 119.62627 119.57013 119.46827 119.32271 119.13473 118.90481 118.63269 118.31733 117.95691
41 42 43 44 45 46 47 48 49 50
117.54885 117.08980 116.57566 116.00152 115.36174 114.64988 113.85877 112.98043 112.00613 110.92638
51 52 53 54 55 56 57 58 59 60
109.73090 108.40865 106.94784 105.33588 103.55943 101.60437 99.45583 97.09815 94.51491 91.68893
61 62 63
88.60223 85.23611 81.57105
$se.fit
1 2 3 4 5 6 7 8 9 10
5.298268 4.370763 3.592101 2.955813 2.455588 2.083260 1.825676 1.662329 1.566864 1.512533
11 12 13 14 15 16 17 18 19 20
1.477572 1.447355 1.413769 1.373549 1.326690 1.275230 1.222382 1.171865 1.127324 1.091786
21 22 23 24 25 26 27 28 29 30
1.067171 1.053981 1.051263 1.056899 1.068100 1.081939 1.095809 1.107729 1.116537 1.121992
31 32 33 34 35 36 37 38 39 40
1.124827 1.126747 1.130363 1.139015 1.156439 1.186260 1.231415 1.293640 1.373218 1.469069
41 42 43 44 45 46 47 48 49 50
1.579082 1.700568 1.830715 1.966988 2.107496 2.251362 2.399124 2.553187 2.718307 2.902040
51 52 53 54 55 56 57 58 59 60
3.115018 3.370894 3.685814 4.077417 4.563601 5.161438 5.886530 6.752911 7.773327 8.959688
61 62 63
10.323512 11.876272 13.629642
$df
[1] 2995
$residual.scale
[1] 39.91479
# Use of ANOVA
fit.1=lm(wage~age,data=Wage)
fit.2=lm(wage~poly(age,2),data=Wage)
fit.3=lm(wage~poly(age,3),data=Wage)
fit.4=lm(wage~poly(age,4),data=Wage)
fit.5=lm(wage~poly(age,5),data=Wage)
anova(fit.1,fit.2,fit.3,fit.4,fit.5) # Analysis of variance
Analysis of Variance Table
Model 1: wage ~ age
Model 2: wage ~ poly(age, 2)
Model 3: wage ~ poly(age, 3)
Model 4: wage ~ poly(age, 4)
Model 5: wage ~ poly(age, 5)
Res.Df RSS Df Sum of Sq F Pr(>F)
1 2998 5022216
2 2997 4793430 1 228786 143.5931 < 2.2e-16 ***
3 2996 4777674 1 15756 9.8888 0.001679 **
4 2995 4771604 1 6070 3.8098 0.051046 .
5 2994 4770322 1 1283 0.8050 0.369682
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
# Or obtain these p-values by exploiting the fact that poly() creates orthogonal polynomials:
coef(summary(fit.5)) # and check P-values for coef of each order
Estimate Std. Error t value Pr(>|t|)
(Intercept) 111.70361 0.7287647 153.2780243 0.000000e+00
poly(age, 5)1 447.06785 39.9160847 11.2001930 1.491111e-28
poly(age, 5)2 -478.31581 39.9160847 -11.9830341 2.367734e-32
poly(age, 5)3 125.52169 39.9160847 3.1446392 1.679213e-03
poly(age, 5)4 -77.91118 39.9160847 -1.9518743 5.104623e-02
poly(age, 5)5 -35.81289 39.9160847 -0.8972045 3.696820e-01
# Generalized linear function
fit=glm(I(wage>250)~poly(age,4),data=Wage,family=binomial) # plus create binary response on the fly with I()
preds=predict(fit,newdata=list(age=age.grid),se=T) # default: type="link" = predictions for logit
# other option type="response" (probability vs odds)
# Step function
table(cut(age,4))
(17.9,33.5] (33.5,49] (49,64.5] (64.5,80.1]
750 1399 779 72
fit=lm(wage~cut(age,4),data=Wage) # cut() returns an ordered categorical variable. Breaks= can be used
coef(summary(fit))
Estimate Std. Error t value Pr(>|t|)
(Intercept) 94.158392 1.476069 63.789970 0.000000e+00
cut(age, 4)(33.5,49] 24.053491 1.829431 13.148074 1.982315e-38
cut(age, 4)(49,64.5] 23.664559 2.067958 11.443444 1.040750e-29
cut(age, 4)(64.5,80.1] 7.640592 4.987424 1.531972 1.256350e-01
fit.1=lm(wage~education+age,data=Wage)
fit.2=lm(wage~education+poly(age,2),data=Wage)
fit.3=lm(wage~education+poly(age,3),data=Wage)
anova(fit.1,fit.2,fit.3)
Analysis of Variance Table
Model 1: wage ~ education + age
Model 2: wage ~ education + poly(age, 2)
Model 3: wage ~ education + poly(age, 3)
Res.Df RSS Df Sum of Sq F Pr(>F)
1 2994 3867992
2 2993 3725395 1 142597 114.6969 <2e-16 ***
3 2992 3719809 1 5587 4.4936 0.0341 *
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
# Splines
library(splines)
fit=lm(wage~bs(age,knots=c(25,40,60)),data=Wage) # 3 knots --> 7 degrees of freedom --> 1 intercept + 6 basis functions
pred=predict(fit,newdata=list(age=age.grid),se=T)
plot(age,wage,col="gray")
lines(age.grid,pred$fit,lwd=2)
lines(age.grid,pred$fit+2*pred$se,lty="dashed")
lines(age.grid,pred$fit-2*pred$se,lty="dashed")

pred=predict(fit,se=T)
plot(age,wage,col="gray")
lines(age,pred$fit,lwd=2) # too many points: that's why we changed age into a list of all unique ages

# That works too - just need to have same length of data in predict() and lines()
age.grid2=sort(unique(age))
pred2=predict(fit,newdata=list(age=age.grid2),se=T)
plot(age,wage,col="gray")
lines(age.grid2,pred2$fit,lwd=2)

str(pred2)
List of 4
$ fit : Named num [1:61] 60.5 62.7 65.8 69.6 73.8 ...
..- attr(*, "names")= chr [1:61] "1" "2" "3" "4" ...
$ se.fit : Named num [1:61] 9.46 5.63 3.78 3.33 3.22 ...
..- attr(*, "names")= chr [1:61] "1" "2" "3" "4" ...
$ df : int 2993
$ residual.scale: num 39.9
dim(bs(age,knots=c(25,40,60), degree = 3)) # bs generates matrix with 6 basis functions (see below)
[1] 3000 6
dim(bs(age,df=6)) # df option to produce a spline with knots at uniform quantiles of the data
[1] 3000 6
attr(bs(age,df=6),"knots")
25% 50% 75%
33.75 42.00 51.00
# Natural spline: ns()
fit2=lm(wage~ns(age,df=4),data=Wage)
pred2=predict(fit2,newdata=data.frame(age=age.grid),se=T)
plot(age,wage,xlim=agelims,cex=.5,col="darkgrey")
lines(age.grid, pred2$fit,col="red",lwd=2)

# Smooth spline
plot(age,wage,xlim=agelims,cex=.5,col="darkgrey")
title("Smoothing Spline")
fit=smooth.spline(age,wage,df=16) # forces 16 degrees of freedom
fit2=smooth.spline(age,wage,cv=TRUE) # choose value of lambda by cross validation --> 6.8 degrees of freedom
cross-validation with non-unique 'x' values seems doubtful
fit2$df
[1] 6.794596
lines(fit,col="red",lwd=2)
lines(fit2,col="blue",lwd=2)
legend("topright",legend=c("16 DF","6.8 DF"),col=c("red","blue"),lty=1,lwd=2,cex=.8)

# Local regresssion (Loess)
plot(age,wage,xlim=agelims,cex=.5,col="darkgrey")
title("Local Regression")
fit=loess(wage~age,span=.2,data=Wage) # span=0.2 means use 20% of observations
fit2=loess(wage~age,span=.5,data=Wage)
lines(age.grid,predict(fit,data.frame(age=age.grid)),col="red",lwd=2)
lines(age.grid,predict(fit2,data.frame(age=age.grid)),col="blue",lwd=2)
legend("topright",legend=c("Span=0.2","Span=0.5"),col=c("red","blue"),lty=1,lwd=2,cex=.8)

LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OgogIGh0bWxfZG9jdW1lbnQ6IGRlZmF1bHQKICBodG1sX25vdGVib29rOiBkZWZhdWx0Ci0tLQoKYGBge3J9CkEgPC0gMTo1CkIgPC0gMTE6MTUKbmFtZXMoQSkgPC0gQQpuYW1lcyhCKSA8LSBCCmBgYAoKYGBge3J9CkEKQgpgYGAKCmBgYHtyfQpWaWV3KGFuc2NvbWJlKQpsbSh5M354MywgZGF0YSA9IGFuc2NvbWJlKQpgYGAKCmBgYHtyfQojIy0tIG5vdyBzb21lICJtYWdpYyIgdG8gZG8gdGhlIDQgcmVncmVzc2lvbnMgaW4gYSBsb29wOgpmZiA8LSB5IH4geAptb2RzIDwtIHNldE5hbWVzKGFzLmxpc3QoMTo0KSwgcGFzdGUwKCJsbSIsIDE6NCkpCmZvcihpIGluIDE6NCkgewogIGZmWzI6M10gPC0gbGFwcGx5KHBhc3RlMChjKCJ5IiwieCIpLCBpKSwgYXMubmFtZSkKICAjIyBvciAgIGZmW1syXV0gPC0gYXMubmFtZShwYXN0ZTAoInkiLCBpKSkKICAjIyAgICAgIGZmW1szXV0gPC0gYXMubmFtZShwYXN0ZTAoIngiLCBpKSkKICBtb2RzW1tpXV0gPC0gbG1pIDwtIGxtKGZmLCBkYXRhID0gYW5zY29tYmUpCiAgcHJpbnQoYW5vdmEobG1pKSkKfQpgYGAKCmBgYHtyfQojIyBTZWUgaG93IGNsb3NlIHRoZXkgYXJlIChudW1lcmljYWxseSEpCnNhcHBseShtb2RzLCBjb2VmKQpsYXBwbHkobW9kcywgZnVuY3Rpb24oZm0pIGNvZWYoc3VtbWFyeShmbSkpKQpgYGAKCmBgYHtyfQojIEFnZ3JlZ2F0ZSBmdW5jdGlvbgojU3BsaXRzIHRoZSBkYXRhIGludG8gc3Vic2V0cywgY29tcHV0ZXMgc3VtbWFyeSBzdGF0aXN0aWNzIGZvciBlYWNoLCBhbmQgcmV0dXJucyB0aGUgcmVzdWx0IGluIGEgY29udmVuaWVudCBmb3JtCnRlc3RERiA8LSBkYXRhLmZyYW1lKHYxID0gYygxLDMsNSw3LDgsMyw1LE5BLDQsNSw3LDkpLAogICAgICAgICAgICAgICAgICAgICB2MiA9IGMoMTEsMzMsNTUsNzcsODgsMzMsNTUsTkEsNDQsNTUsNzcsOTkpICkKYnkxIDwtIGMoInJlZCIsICJibHVlIiwgMSwgMiwgTkEsICJiaWciLCAxLCAyLCAicmVkIiwgMSwgTkEsIDEyKQpieTIgPC0gYygid2V0IiwgImRyeSIsIDk5LCA5NSwgTkEsICJkYW1wIiwgOTUsIDk5LCAicmVkIiwgOTksIE5BLCBOQSkKYWdncmVnYXRlKHggPSB0ZXN0REYsIGJ5ID0gbGlzdChieTEsIGJ5MiksIEZVTiA9ICJtZWFuIikKCiMgb3IgZnJvbSBUaXRhbmljIEthZ2cKI2FnZ3JlZ2F0ZShTdXJ2aXZlZCB+IEZhcmUyICsgUGNsYXNzICsgU2V4LCBkYXRhPXRyYWluLCBGVU49ZnVuY3Rpb24oeCkge3N1bSh4KS9sZW5ndGgoeCl9KQpgYGAKCmBgYHtyfQojIyBOb3csIGRvIHdoYXQgeW91IHNob3VsZCBoYXZlIGRvbmUgaW4gdGhlIGZpcnN0IHBsYWNlOiBQTE9UUwpvcCA8LSBwYXIobWZyb3cgPSBjKDIsIDIpLCBtYXIgPSAwLjErYyg0LDQsMSwxKSwgb21hID0gIGMoMCwgMCwgMiwgMCkpCmZvcihpIGluIDE6NCkgewogIGZmWzI6M10gPC0gbGFwcGx5KHBhc3RlMChjKCJ5IiwieCIpLCBpKSwgYXMubmFtZSkKICBwbG90KGZmLCBkYXRhID0gYW5zY29tYmUsIGNvbCA9ICJyZWQiLCBwY2ggPSAyMSwgYmcgPSAib3JhbmdlIiwgY2V4ID0gMS4yLAogICAgICAgeGxpbSA9IGMoMywgMTkpLCB5bGltID0gYygzLCAxMykpCiAgYWJsaW5lKG1vZHNbW2ldXSwgY29sID0gImJsdWUiKQp9Cm10ZXh0KCJBbnNjb21iZSdzIDQgUmVncmVzc2lvbiBkYXRhIHNldHMiLCBvdXRlciA9IFRSVUUsIGNleCA9IDEuNSkKcGFyKG9wKQpgYGAKCmBgYHtyfQp4ID0gYyhUUlVFLCBGQUxTRSwgRkFMU0UpCnR5cGVvZih4KSAgI2xvZ2ljYWwgKHZlY3RvcikKbW9kZSh4KQpzdG9yYWdlLm1vZGUoeCkKeSA9IDE6MTAKdHlwZW9mKHkpCm1vZGUoeSkKc3RvcmFnZS5tb2RlKHkpCmBgYAoKYGBge3J9CmRpbSh5KQpsZW5ndGgoeSkKYGBgCgpgYGB7cn0Kej0gbGlzdCgxLCBUUlVFLCAnc2FmcycpICN0cnlpbmcgdG8gZ2V0IGEgbGlzdAp0eXBlb2YoeikKY2xhc3MoeikKelszXQpsZW5ndGgoeikKZGltKHopCgpgYGAKCmBgYHtyfQpxdW90ZSh4K3kpCmFzLmxpc3QocXVvdGUoeCArIHkpKQpgYGAKCmBgYHtyfQoxZTNMICNjcmVhdGUgY29uc3RhbnQKMUwKYGBgCgpgYGB7cn0KY2F0KDErMikKJysnKDEsIDIpCmBgYAoKYGBge3J9CnggPC0gb3B0aW9ucygpCngkcHJvbXB0CmBgYAoKYGBge3J9Cm1hdGNoKE5BLCBOYU4pCm1hdGNoKE5BLCBOQSkKbWF0Y2goTmFOLCBOYU4pCmBgYAoKYGBge3J9CnggPSBhcnJheSgxOjgsIGMoMiw0KSkKeAppPTIKaj0zCnhbaV0KeFtpLCBqXQp4W1tpXV0KeFtbaSwgal1dCmBgYAoKYGBge3J9CnJvd25hbWVzKHgpPWMoImEiLCJiIikKeAp4ID0gYXMuZGF0YS5mcmFtZSh4KQp4CnhbImEiLF0KeFtdCmBgYAoKYGBge3J9CmkgPC0gbWF0cml4KDE6NCwgMiwgYnlyb3cgPSBUUlVFKQppCmBgYAoKYGBge3J9CmlbMixdCmlbMiwgLGRyb3A9RkFMU0VdICMga2VlcGluZyBkaW1lbnNpb24gMSAqIG4gd2hlbiBzZWxlY3Rpb24gYSByb3cKZGltKGlbMixdKQpkaW0oaVsyLCAsZHJvcD1GQUxTRV0pCmBgYAoKYGBge3J9CiMgaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvZG9jL21hbnVhbHMvUi1pbnRyby5wZGYKCmhlbHAuc3RhcnQoKQpgYGAKCmBgYHtyfQp4IDwtIHJub3JtKDEwMDAwKQp5IDwtIHJub3JtKHgpCnBsb3QoeCwgeSkKaGlzdCh5KQpgYGAKCmBgYHtyfQpscygpCmBgYAoKYGBge3J9CnJtKGxpc3Q9bHMoKSkKbHMoKQpgYGAKCmBgYHtyfQp4IDwtIDE6MjAKdyA8LSAxICsgc3FydCh4KS8yCmR1bW15IDwtIGRhdGEuZnJhbWUoeD14LCB5PSB4ICsgcm5vcm0oeCkqdykKZHVtbXkgCmBgYAoKYGBge3J9CiMgNCBPcmRlcmVkIGFuZCB1bm9yZGVyZWQgZmFjdG9ycwpzdGF0ZSA8LSBjKCJ0YXMiLCAic2EiLCAicWxkIiwgIm5zdyIsICJuc3ciLCAibnQiLCAid2EiLCAid2EiLAoicWxkIiwgInZpYyIsICJuc3ciLCAidmljIiwgInFsZCIsICJxbGQiLCAic2EiLCAidGFzIiwKInNhIiwgIm50IiwgIndhIiwgInZpYyIsICJxbGQiLCAibnN3IiwgIm5zdyIsICJ3YSIsCiJzYSIsICJhY3QiLCAibnN3IiwgInZpYyIsICJ2aWMiLCAiYWN0IikKc3RhdGVmIDwtIGZhY3RvcihzdGF0ZSkKc3RhdGVmCmBgYAoKYGBge3J9CmxldmVscyhzdGF0ZWYpCmBgYAoKYGBge3J9CmluY29tZXMgPC0gYyg2MCwgNDksIDQwLCA2MSwgNjQsIDYwLCA1OSwgNTQsIDYyLCA2OSwgNzAsIDQyLCA1NiwKNjEsIDYxLCA2MSwgNTgsIDUxLCA0OCwgNjUsIDQ5LCA0OSwgNDEsIDQ4LCA1MiwgNDYsCjU5LCA0NiwgNTgsIDQzKQppbmNtZWFucyA8LSB0YXBwbHkoaW5jb21lcywgc3RhdGVmLCBtZWFuKQppbmNtZWFucwpgYGAKCmBgYHtyfQojIEFycmF5cwphIDwtIGFycmF5KDE6MzAsIGRpbT1jKDIsIDUsMykpCmEKYGBgCgpgYGB7cn0KeCA8LSBhcnJheSgxOjIwLCBkaW09Yyg0LDUpKSAjIEdlbmVyYXRlIGEgNCBieSA1IGFycmF5Lgp4CmkgPC0gYXJyYXkoYygxOjMsMzoxKSwgZGltPWMoMywyKSkKaQp4W2ldICNnZXQgdGhlIDMgZWxlbWVudHMgc2hvd24gYnkgaTogKDMsIDEpLCAoMiwgMikgYW5kICgxLCAzKQpgYGAKCmBgYHtyfQpoZWxwKCI8LSIpCmBgYAoKYGBge3J9CiMgQmFjayB0byBodHRwOi8vem9vbmVrMi5mcmVlLmZyL1VOSVgvNDhfUi8wMi5odG1sCnggPC0gcm5vcm0oMTApCngKc29ydCh4KQpvcmRlcih4KQp4W29yZGVyKHgpXQpgYGAKCmBgYHtyfQp4IDwtIHNhbXBsZSgxOjUsIDEwLCByZXBsYWNlPVQpCngKeFtvcmRlcih4KV0KdW5pcXVlKHgpCmBgYAoKYGBge3J9CnNlcSgwLDEwLCBsZW5ndGg9MTEpID09IHNlcSgwLDEwLCBieT0xKQpgYGAKCmBgYHtyfQojIFJlcApyZXAoMCwgMTApCnJlcCgxOjUsMykKcmVwKDE6NSwgZWFjaD0zKQpyZXAoMTo1LDIsZWFjaD0zKQpgYGAKCmBgYHtyfQojIEZhY3Rvcgp4IDwtIGZhY3Rvciggc2FtcGxlKGMoIlllcyIsICJObyIsICJQZXJoYXBzIiksIDUsIHJlcGxhY2U9VCkgKQp4CmBgYAoKYGBge3J9CiMgc3BlY2lmeSBsZXZlbHMKbCA8LSBjKCJZZXMiLCAiTm8iLCAiUGVyaGFwcyIpCnggPC0gZmFjdG9yKCBzYW1wbGUobCwgNSwgcmVwbGFjZT1UKSwgbGV2ZWxzPWwgKQp4CmBgYAoKYGBge3J9CnRhYmxlKHgpCmBgYAoKYGBge3J9CiMgZ2w6IEdlbmVyYXRlIEZhY3RvciBMZXZlbHMKZ2woMSw0KQpnbCgyLDQpCmdsKDIsMSw4KQpnbCgyLDEsOCwgbGFiZWxzPWMoVCxGKSkKYGBgCgpgYGB7cn0KeCA8LSBnbCgyLDQpCngKeSA8LSBnbCgyLDEsOCkKeQppbnRlcmFjdGlvbih4LHkpCmRhdGEuZnJhbWUoeCx5LCBpbnQ9aW50ZXJhY3Rpb24oeCx5KSkKYGBgCgpgYGB7cn0KIyBDYXJ0ZXNpYW4gcHJvZHVjdCAodG91dGVzIGxlcyBwb3NzaWJpbGl0ZXMpCnggPC0gYygiQSIsICJCIiwgIkMiKQp5IDwtIDE6Mgp6IDwtIGMoImEiLCAiYiIpCmV4cGFuZC5ncmlkKHgseSx6KQpgYGAKCmBgYHtyfQp4IDwtIGZhY3RvcihjKDMsNCw1LDEpKQphcy5udW1lcmljKCBsZXZlbHMoeCkpCmFzLm51bWVyaWMoIGxldmVscyh4KVsgeCBdICkgIyBwcm9wZXIgd2F5IHRvIGNvbnZlcnQgZnJvbSBmYWN0b3IgdG8gbnVtZXJpYwpgYGAKCmBgYHtyfQojIERhdGEgRnJhbWVzCm4gPC0gMTAKZGYgPC0gZGF0YS5mcmFtZSggeD1ybm9ybShuKSwgeT1zYW1wbGUoYyhULEYpLG4scmVwbGFjZT1UKSApCnN0cihkZikKYGBgCgpgYGB7cn0Kc3VtbWFyeShkZikKYGBgCgpgYGB7cn0KbmFtZXMoZGYpO2NhdChyb3duYW1lcyhkZikpCmBgYAoKYGBge3J9CiMgTWVyZ2UKbWVyZ2UoeCwgeSkgICAgICAgICAgICAgICAgICMgSU5ORVIgSk9JTgptZXJnZSh4LCB5LCBhbGwueCA9IFRSVUUpICAgIyBMRUZUIEpPSU4KbWVyZ2UoeCwgeSwgYWxsLnkgPSBUUlVFKSAgICMgUklHSFQgSk9JTgptZXJnZSh4LCB5LCBhbGwgICA9IFRSVUUpICAgIyBPVVRFUiBKT0lOCiNtZXJnZShhLCBiLCBieT1jKCJ5IiwgInoiKSkgIyBzcGVjaWZ5IHdoYXQgdG8gbWVyZ2Ugb24KYGBgCgpgYGB7cn0KIyBSZWdyZXNzaW9uCmRhdGEoY2FycykgCiNWaWV3KGNhcnMpCgojIFJlZ3Jlc3Npb24KbG0uZml0PWxtKCBkaXN0IH4gc3BlZWQsIGRhdGE9Y2FycywgbmEuYWN0aW9uID0gbmEuZXhjbHVkZSkKbG0uZml0CiMgUG9seW5vbWlhbCByZWdyZXNzaW9uCmxtLmZpdDMgPSBsbSggZGlzdCB+IHBvbHkoc3BlZWQsMyksIGRhdGE9Y2FycykKCiNwbG90KGxtLmZpdCkKcGxvdChjYXJzJHNwZWVkLCBjYXJzJGRpc3QpCmFibGluZShsbS5maXQpCmBgYAoKYGBge3J9CiMgVHJlZXMKIyBpbnN0YWxsLnBhY2thZ2VzKCdyYXR0bGUnKQojIGluc3RhbGwucGFja2FnZXMoJ3JwYXJ0LnBsb3QnKQojIGluc3RhbGwucGFja2FnZXMoJ1JDb2xvckJyZXdlcicpCmxpYnJhcnkocmF0dGxlKQpsaWJyYXJ5KHJwYXJ0LnBsb3QpCmxpYnJhcnkoUkNvbG9yQnJld2VyKQpgYGAKCmBgYHtyfQojIGh0dHA6Ly90cmV2b3JzdGVwaGVucy5jb20va2FnZ2xlLXRpdGFuaWMtdHV0b3JpYWwvci1wYXJ0LTUtcmFuZG9tLWZvcmVzdHMvCiMgZml0IDwtIHJwYXJ0KFN1cnZpdmVkIH4gUGNsYXNzICsgU2V4ICsgQWdlICsgU2liU3AgKyBQYXJjaCArIEZhcmUgKyBFbWJhcmtlZCwKIyAgICAgICAgICAgICAgICBkYXRhPXRyYWluLAojICAgICAgICAgICAgICAgIG1ldGhvZD0iY2xhc3MiLCAKIyAgICAgICAgICAgICAgICBjb250cm9sPXJwYXJ0LmNvbnRyb2wobWluc3BsaXQ9MiwgY3A9MCkpCiMgZmFuY3lScGFydFBsb3QoZml0KQoKIyBQcmVkaWN0IGEgY29udGludW91cyB2YXJpYWJsZSAobWV0aG9kIGFub3ZhKQojIEFnZWZpdCA8LSBycGFydChBZ2UgfiBQY2xhc3MgKyBTZXggKyBTaWJTcCArIFBhcmNoICsgRmFyZSArIEVtYmFya2VkICsgVGl0bGUgKyBGYW1pbHlTaXplLAojICAgICAgICAgICAgICAgICAgIGRhdGE9Y29tYmlbIWlzLm5hKGNvbWJpJEFnZSksXSwgCiMgICAgICAgICAgICAgICAgICAgbWV0aG9kPSJhbm92YSIpCmBgYAoKYGBge3J9CiMgTGlzdHMKaCA8LSBsaXN0KCkKaFtbImZvbyJdXSA8LSAxCmhbWyJiYXIiXV0gPC0gYygiYSIsICJiIiwgImMiKQpoWyJiYXIiXSA9PSBoW1siYmFyIl1dICNoWyJiYXIiXSBpcyBhIGxpc3QgY29udGFpbmluZyB0aGUgdmVjdG9yCmBgYAoKYGBge3J9CiMgRGVsZXRlCmhbWyJiYXIiXV0gPC0gTlVMTApgYGAKCmBgYHtyfQptIDwtIG1hdHJpeCggYygxLDIsMyw0KSwgbnJvdz0yICkKbQpgYGAKCmBgYHtyfQpzb2x2ZShtKQp4IDwtIG1hdHJpeCggYyg2LDcpLCBucm93PTIgKQpzb2x2ZShtLCB4KQpgYGAKCmBgYHtyfQo/c29sdmUKYGBgCgpgYGB7cn0KbiA8LSAxMDAwCngxIDwtIGZhY3Rvciggc2FtcGxlKDE6MywgbiwgcmVwbGFjZT1UKSwgbGV2ZWxzPTE6MyApCngyIDwtIGZhY3Rvciggc2FtcGxlKExFVFRFUlNbMTo1XSwgbiwgcmVwbGFjZT1UKSwgbGV2ZWxzPUxFVFRFUlNbMTo1XSApCngzIDwtIGZhY3Rvciggc2FtcGxlKGMoRixUKSxuLHJlcGxhY2U9VCksIGxldmVscz1jKEYsVCkgKQpkIDwtIGRhdGEuZnJhbWUoeDEseDIseDMpCnIgPC0gdGFibGUoZCkKYGBgCgpgYGB7cn0KcgpgYGAKCmBgYHtyfQpmdGFibGUoZCkgI2Vhc2llciByZWFkaW5nCmBgYAoKYGBge3J9CiMgY29udGluZ2VuY3kgdGFibGUgaW50byBhIGRhdGEuZnJhbWUKbiA8LSAxMDAKayA8LSAxMAp4IDwtIGZhY3Rvciggc2FtcGxlKExFVFRFUlNbMTprXSwgbiwgcmVwbGFjZT1UKSwgbGV2ZWxzPUxFVFRFUlNbMTprXSApCngKZCA8LSB0YWJsZSh4KQp4MiA9IGZhY3RvciggcmVwKG5hbWVzKGQpLGQpLCBsZXZlbHM9bmFtZXMoZCkgKQp4MgpgYGAKCmBgYHtyfQojIGFwcGx5Cm9wdGlvbnMoZGlnaXRzPTQpCmRmIDwtIGRhdGEuZnJhbWUoeD1ybm9ybSgyMCkseT1ybm9ybSgyMCksej1ybm9ybSgyMCkpCmFwcGx5KGRmLDIsbWVhbikKcm93bmFtZXMoZGYpIDwtIExFVFRFUlNbMToyMF0KYXBwbHkoZGYsIDEsIG1lYW4pCmBgYAoKYGBge3J9CmdsKDIsMTAsMjApCnRhcHBseSgxOjIwLCBnbCgyLDEwLDIwKSwgc3VtKSAjIHRhcHBseTogMm5kIGFyZ3VtZW50IHVzZWQgZm9yIGdyb3VwaW5nCgpieSgxOjIwLCBnbCgyLDEwLDIwKSwgc3VtKQpgYGAKCmBgYHtyfQp4IDwtIGxpc3QoYT1ybm9ybSgxMCksIGI9cnVuaWYoMTAwKSwgYz1yZ2FtbWEoNTAsMSkpCnNhcHBseSh4LHNkKSAjIHNhcHBseTogYXBwbHkgRlVOIG9uIGVhY2ggZWxlbWVudCBvZiB2ZWN0b3IKbGFwcGx5KHgsc2QpICMgbGFwcGx5OiBzYW1lIGJ1dCByZXR1cm5zIGxpc3QKCmBgYAoKYGBge3J9CiMgRXhlcmNpc2U6IExldCB4IGJlIGEgYm9vbGVhbiB2ZWN0b3IuIENvdW50IHRoZSBudW1iZXIgb2Ygc2VxdWVuY2VzICgicnVucyIpIG9mIHplcm9zIChmb3IgaW5zdGFuY2UsIGluIDAwMTAxMDAxMDEwMTEwLCB0aGVyZSBhcmUgNiBydW5zOiAwMCAwIDAwIDAgMCAwKS4gQ291bnQgdGhlIG51bWJlciBvZiBzZXF1ZW5jZXMgb2YgMS4gQ291bnRoIHRoZSB0b3RhbCBudW1iZXIgb2Ygc2VxdWVuY2VzLiBTYW1lIHF1ZXN0aW9uIGZvciBhIGZhY3RvciB3aXRoIG1vcmUgdGhhbSB0d28gbGV2ZWxzLgpuIDwtIDUwCnggPC0gc2FtcGxlKDA6MSwgbiwgcmVwbGFjZT1ULCBwPWMoLjIsLjgpKQp4CmRpZmYoeCwgbGFnPTEpCgoKYGBgCgpgYGB7cn0KI0xldCByIGJlIHRoZSByZXR1cm4gb2YgYSBmaW5hbmNpYWwgYXNzZXQuIFRoZSBjbHVzdGVyZWQgcmV0dXJuIGlzIHRoZSBhY2N1bXVsYXRlZCByZXR1cm4gZm9yIGEgc2VxdWVuY2Ugb2YgcmV0dXJucyBvZiB0aGUgc2FtZSBzaWduLiBUaGUgdHJlbmQgbnVtYmVyIGlzIHRoZSBudW1iZXIgb2Ygc3RlcHMgaW4gc3VjaCBhIHNlcXVlbmNlLiBUaGUgYXZlcmFnZSByZXR1cm4gaXMgdGhlaXIgcmF0aW8uIENvbXB1dGUgdGhlc2UgcXVhbnRpdGllcy4KZGF0YShFdVN0b2NrTWFya2V0cykKeCA8LSBFdVN0b2NrTWFya2V0cwojIFdlIGFyZW4ndCBpbnRlcmVzdGVkIGluIHRoZSBzcG90IHByaWNlcywgYnV0IGluIHRoZSByZXR1cm5zCiMgcmV0dXJuW2ldID0gKCBwcmljZVtpXSAtIHByaWNlW2ktMV0gKSAvIHByaWNlW2ktMV0KeSA8LSBhcHBseSh4LCAyLCBmdW5jdGlvbiAoeCkgeyBkaWZmKHgpL3hbLWxlbmd0aCh4KV0gfSkKIyBXZSBub3JtYWxpemUgdGhlIGRhdGEKeiA8LSBhcHBseSh5LCAyLCBmdW5jdGlvbiAoeCkgeyAoeC1tZWFuKHgpKS9zZCh4KSB9KQojIEEgc2luZ2xlIHRpbWUgc2VyaWVzCnIgPC0gelssMV0KIyBUaGUgcnVucwpmIDwtIGZhY3RvcihjdW1zdW0oYWJzKGRpZmYoc2lnbihyKSkpKS8yKQpyIDwtIHJbLTFdCmFjY3VtdWxhdGVkLnJldHVybiA8LSB0YXBwbHkociwgZiwgc3VtKQp0cmVuZC5udW1iZXIgPC0gdGFibGUoZikKYm94cGxvdChhYnMoYWNjdW11bGF0ZWQucmV0dXJuKSB+IHRyZW5kLm51bWJlciwgY29sPSdwaW5rJywKICAgICAgICBtYWluPSJBY2N1bXVsYXRlZCByZXR1cm4iKQpgYGAKCmBgYHtyfQojIFN0cmluZ3MKcHJpbnQoIkhlbGxvXG4iKQoKY2F0KCJIZWxsb1xuIikgI3VzZSBjYXQKYGBgCgpgYGB7cn0KcGFzdGUoIkhlbGxvIiwgIldvcmxkIiwgIiEiLCBzZXA9IiIpICNjb25jYXRlbmF0ZQpwYXN0ZSgiSGVsbG8gIiwgIiBXb3JsZCIsICIhIiwgc2VwPSIiKQpgYGAKCmBgYHtyfQp4IDwtIDUKcGFzdGUoIng9IiwgeCkKY2F0KCJ4PSIsIHgsICJcbiIsIHNlcD0iXG4iKQpgYGAKCmBgYHtyfQpzIDwtIGMoIkhlbGxvIiwgIiAiLCAiV29ybGQiLCAiISIpCnBhc3RlKHMpCnBhc3RlKHMsIHNlcD0iIikKcGFzdGUocywgY29sbGFwc2U9IiIpCmBgYAoKYGBge3J9CnBhc3RlKDE6MywgIkhlbGxvIFdvcmxkISIsIHNlcD0iOiIpCmBgYAoKYGBge3J9Cm5jaGFyKCJIZWxsbyBXb3JsZCEiKQpgYGAKCmBgYHtyfQpzIDwtICJIZWxsbyBXb3JsZCIKc3Vic3RyaW5nKHMsIDQsIDYpCmBgYAoKYGBge3J9CnMgPC0gImZvby0tPmJhci0tPmJheiIKc3Ryc3BsaXQocywgIi0tPiIpCmBgYAoKYGBge3J9CiMgUmVnZXgKcyA8LSAiZm9vLCBiYXIsIGJheiIKc3Ryc3BsaXQocywgIiwgKiIpCmBgYAoKYGBge3J9CnMgPC0gYXBwbHkobWF0cml4KExFVFRFUlNbMToyNF0sIG5yPTQpLCAyLCBwYXN0ZSwgY29sbGFwc2U9IiIpCnMKYGBgCgpgYGB7cn0KZ3JlcCgiTyIsIHMpCmdyZXAoIk8iLCBzLCB2YWx1ZT1UKQpgYGAKCmBgYHtyfQpyZWdleHByKCJvIiwgIkhlbGxvIikKYGBgCgpgYGB7cn0KcmVnZXhwcigibyIsIGMoIkhlbGxvIiwgIldvcmxkISIpKQpgYGAKCmBgYHtyfQpzIDwtICJmb28gICAgYmFyIGJheiIKZ3N1YigiICIsICIiLCBzKSAgICMgUmVtb3ZlIGFsbCB0aGUgc3BhY2VzCmdzdWIoIiArIiwgIiAiLCBzKSAgIyBSZW1vdmUgbXVsdGlwbGUgc3BhY2VzIGFuZCByZXBsYWNlIHRoZW0gYnkgc2luZ2xlIHNwYWNlcwoKYGBgCgpgYGB7cn0KI1RoZSAic3ViIiBpcyBzaW1pbGFyIHRvICJnc3ViIiBidXQgb25seSByZXBsYWNlcyB0aGUgZmlyc3Qgb2NjdXJyZW5jZS4KcyA8LSAiZm9vIGJhciBiYXoiCnN1YigiICIsICIiLCBzKQpgYGAKCmBgYHtyfQojIERhdGVzCmFzLkRhdGUoIjIwMDUtMDUtMTUiKSAjSVNPIDg2MDEKYGBgCgpgYGB7cn0KYXMuRGF0ZSgiMTUvMDUvMjAwNSIsIGZvcm1hdD0iJWQvJW0vJVkiKQphcy5EYXRlKCIxNS8wNS8wNSIsIGZvcm1hdD0iJWQvJW0vJXkiKQpjYXQoIlxuIikKYXMuRGF0ZSgiMDEvMDIvMDMiLCBmb3JtYXQ9IiV5LyVtLyVkIikKYXMuRGF0ZSgiMDEvMDIvMDMiLCBmb3JtYXQ9IiV5LyVkLyVtIikKCmBgYAoKYGBge3J9CmFzLkRhdGUoIjAxLzAyLzAzIiwgZm9ybWF0PSIleS8lbS8lZCIpIC0gYXMuRGF0ZSgiMDEvMDIvMDMiLCBmb3JtYXQ9IiV5LyVkLyVtIikKYGBgCgpgYGB7cn0KU3lzLkRhdGUoKQpmb3JtYXQoU3lzLkRhdGUoKSwgZm9ybWF0PSIlQSwgJWQgJUIgJVkiKQpgYGAKCmBgYHtyfQpzZXEoYXMuRGF0ZSgiMjAwNS0wMS0wMSIpLCBhcy5EYXRlKCIyMDA1LTA3LTAxIiksIGJ5PSJtb250aCIpCnNlcShhcy5EYXRlKCIyMDA1LTAxLTAxIiksIGFzLkRhdGUoIjIwMDUtMDctMDEiKSwgYnk9MzEpCmBgYAoKYGBge3J9Cm1ldGhvZHMoY2xhc3M9IkRhdGUiKQpgYGAKCmBgYHtyfQphcy5QT1NJWGx0KCIyMDA1LTA1LTE1IDIxOjQ1OjE3IikKYGBgCgpgYGB7cn0KYXMuUE9TSVhjdChTeXMuRGF0ZSgpKQpgYGAKCmBgYHtyfQojIFJlYWRpbmcgZnJvbSBkYXRhZnJhbWVzCiMgb3B0aW9uIDEKICAjZCA8LSByZWFkLnRhYmxlKCJmb28udHh0IikKICAjZCREYXRlIDwtIGFzLkRhdGUoIGFzLmNoYXJhY3RlciggZCREYXRlICkgKQojIG9wdGlvbiAyCiAgI3JlYWQudGFibGUoImZvby50eHQiLCBjb2xDbGFzc2VzPWMoIkRhdGUiLCAiY2hhcmFjdGVyIiwgcmVwKDEwLCAibnVtZXJpYyIpKSkKYGBgCgpgYGB7cn0KCmBgYAoKYGBge3J9Cm9wdGlvbnMod2Fybj0xKQpgYGAKCmBgYHtyfQptZXRob2RzKHBsb3QpCmBgYAoKYGBge3J9CiMgSW1wb3J0IAoKIyBkIDwtIHJlYWQudGFibGUoImZvby50eHQiLCBoZWFkZXI9VCwgc2VwPSIsIikKIyBkIDwtIHJlYWQuY3N2KCJ0eHQuY3N2IikKIyBkIDwtIHJlYWQuY3N2MigidHh0LmNzdiIpICAjIHNlbWljb2xvbi1zZXBhcmF0ZWQgZmlsZSwgd2l0aCBhCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBjb21tYSBpbnN0ZWFkIG9mIHRoZSBkZWNpbWFsIHBvaW50LgojIGQgPC0gcmVhZC5kZWxpbSgiZm9vLnR4dCIpICMgVGFiLWRlbGltaXRlZCBmaWxlCiMgZCA8LSByZWFkLmZ3ZigidHh0LmZ3ZiIpICAgIyBGaXhlZCB3aWR0aCBmaWVsZHMKYGBgCgpgYGB7cn0KIyBFeGNlbDogdGhpcyBtYXkgYmUgdHJpY2tpZXI6IHRoZSBtaXNzaW5nIHZhbHVlcyBvZnRlbiBhcHBlYXIgYXMgIiNOL0EhIiBhbmQgYXJlIG1pc3Rha2VuIGZvciB0aGUgc3RhcnQgb2YgYSBjb21tZW50Li4uIFlvdSBjYW4gdHJ5CgojIGQgPC0gcmVhZC50YWJsZSgiZm9vLmNzdiIsIGhlYWRlciA9IFRSVUUsIHNlcCA9ICIsIiwgCiMgICAgICAgICAgICAgICAgIG5hLnN0cmluZ3MgPSBjKCIjTi9BISIsICJOQSIsICJATkEiKSwgCiMgICAgICAgICAgICAgICAgIHF1b3RlID0gJyInLAojICAgICAgICAgICAgICAgICBjb21tZW50LmNoYXIgPSAiIikKYGBgCgpgYGB7cn0KI0lmIHlvdXIgZmlsZSBvbmx5IGNvbnRhaW5zIG51bWJlciwgb3Igb25seSBzdHJpbmdzLCBpdCBpcyB3aXNlciB0byBzdG9yZSBpdCBpbiBhIG1hdHJpeCwgbm90IGEgZGF0YS5mcmFtZS4gVGhpcyBpcyB3aGF0IHRoZSAic2NhbiIgZnVuY3Rpb24gZG9lcy4KIyBBIG51bWVyaWMgbWF0cml4CiAgIyB4IDwtIHNjYW4oImZvby50eHQiLCBzZXA9IiwiKSAgIyBHaXZlcyBhIG51bWVyaWMgdmVjdG9yCiAgIyBuIDwtIHNjYW4oImZvby50eHQiLCBzZXA9IiwiLCBubGluZXM9MSkKICAjIHggPC0gbWF0cml4KHgsIG5jPW4pCgojIEEgdmVjdG9yIG9mIHN0cmluZ3MKICAjeCA8LSBzY2FuKCJmb28udHh0Iiwgd2hhdD1jaGFyYWN0ZXIoMCkpCgpgYGAKCmBgYHtyfQojIEJhY2sgdG9odHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy9kb2MvbWFudWFscy9SLWludHJvLnBkZiAtIFJlZ3Jlc3Npb24KCiAgIyBmbTA1IDwtIGxtKHkgfiB4MSArIHgyICsgeDMgKyB4NCArIHg1LCBkYXRhID0gcHJvZHVjdGlvbikKICAjIGZtNiA8LSB1cGRhdGUoZm0wNSwgLiB+IC4gKyB4NikKICAjIHNtZjYgPC0gdXBkYXRlKGZtNiwgc3FydCguKSB+IC4pCmBgYAoKYGBge3J9CiMgU3BpbmUgKGZyb20gaGVscCkKcmVxdWlyZShncmFwaGljcykKCm9wIDwtIHBhcihtZnJvdyA9IGMoMiwxKSwgbWdwID0gYygyLC44LDApLCBtYXIgPSAwLjErYygzLDMsMywxKSkKbiA8LSA5CnggPC0gMTpuCnkgPC0gcm5vcm0obikKcGxvdCh4LCB5LCBtYWluID0gcGFzdGUoInNwbGluZVtmdW5dKC4pIHRocm91Z2giLCBuLCAicG9pbnRzIikpCmxpbmVzKHNwbGluZSh4LCB5KSkKbGluZXMoc3BsaW5lKHgsIHksIG4gPSAyMTApLCBjb2wgPSAyKQpgYGAKCmBgYHtyfQojIE5BIGhhbmRsaW5nIC0gaHR0cDovL3Rob21hc2xlZXBlci5jb20vUmNvdXJzZS9UdXRvcmlhbHMvTkEuaHRtbApnMSA8LSBjKDEsIDIsIE5BLCBOQSwgTkEsIDYsIDcpCmcyIDwtIG5hLm9taXQoZzEpCmcyCmBgYAoKYGBge3J9CmF0dHJpYnV0ZXMoZzIpJG5hLmFjdGlvbgpgYGAKCmBgYHtyfQpzdW0oZzEpCnN1bShnMSwgbmEucm0gPSBUUlVFKQpgYGAKCmBgYHtyfQojIENvciAtPiBjYW4gZWxpbWluYXRlIG9ubHkgcGFpci13aXNlIE5BcyAoKQp4IDwtIGMoMSwgMiwgMywgTkEsIDUsIDcsIDkpCnkgPC0gYygzLCAyLCA0LCA1LCAxLCAzLCA0KQp6IDwtIGMoTkEsIDIsIDMsIDUsIDQsIDMsIDQpCm0gPC0gZGF0YS5mcmFtZSh4LCB5LCB6KQptCmBgYAoKYGBge3J9CmNvcihtKSAgIyByZXR1cm5zIGFsbCBOQXMKYGBgCgpgYGB7cn0KY29yKG0sIHVzZSA9ICJjb21wbGV0ZS5vYnMiKSAjIERlZmF1bHQsIGFsbCByZWNvcmRzIHdpdGggTkEgcmVtb3ZlZApgYGAKCmBgYHtyfQpjb3IobSwgdXNlID0gInBhaXJ3aXNlLmNvbXBsZXRlLm9icyIpICNrZXB0IG1vcmUgcmVjb3JkcyBmb3IgeX54IGFuZCB5fnoKYGBgCgpgYGB7cn0KIyBEZWZhdXQgZm9yIGxtIGlzIGFsc28gY2FzZXdpc2UgZGVsZXRpb24KbG0gPC0gbG0oeSB+IHggKyB6LCBkYXRhID0gbSkKc3VtbWFyeShsbSkKYGBgCgpgYGB7cn0KIyBDaGVja2luZyBsZW5ndGggb2YgZGF0YSB1c2VkIGZvciByZWdyZXNzaW9uCmxlbmd0aChtJHkpCmxlbmd0aChsbSRmaXR0ZWQpCmBgYAoKYGBge3J9CiMgY2hlY2tpbmcgd2hlcmUgbWlzc2luZyBkYXRhIGlzCmlzLm5hKG0pICMgb25seSB1c2VmdWwgZm9yIHNtYWxsIGV4YW1wbGVzCiMgaW1hZ2UKaW1hZ2UoaXMubmEobSksIG1haW4gPSAiTWlzc2luZyBWYWx1ZXMiLCB4bGFiID0gIk9ic2VydmF0aW9uIiwgeWxhYiA9ICJWYXJpYWJsZSIsIAogICAgeGF4dCA9ICJuIiwgeWF4dCA9ICJuIiwgYnR5ID0gIm4iKQpheGlzKDEsIHNlcSgwLCAxLCBsZW5ndGgub3V0ID0gbnJvdyhtKSksIDE6bnJvdyhtKSwgY29sID0gIndoaXRlIikKYXhpcygyLCBjKDAsIDAuNSwgMSksIG5hbWVzKG0pLCBjb2wgPSAid2hpdGUiLCBsYXMgPSAyKQpgYGAKCmBgYHtyfQojIHRvIHJlbW92ZSBjYXNld2lzZToKbTIgPC0gbmEub21pdChtKSAjIHVzZSBuZXcgdmFyaWFibGUgdG8ga2VlcCBvcmlnaW5hbCBkYXRhZnJhbWUKbQptMgpgYGAKCmBgYHtyfQojIE1lYW4gaW1wdXRhdGlvbgp4MiA8LSB4CngyW2lzLm5hKHgyKV0gPC0gbWVhbih4MiwgbmEucm0gPSBUUlVFKQp4CngyCmBgYAoKYGBge3J9CiMgUmFuZG9tIGltcHV0YXRpb24gLSBjb25zZXJ2ZSBtZWFuIGFuZCB2YXJpYW5jZS4gCiMgSG93OiBzYW1wbGUgcmVzdCBvZiB0aGUgdmFsdWVzIHRvIGZpbGwgTkFzCngzIDwtIHgKeDNbaXMubmEoeDMpXSA8LSBzYW1wbGUoeDNbIWlzLm5hKHgzKV0sIHN1bShpcy5uYSh4MykpLCBUUlVFKQp4CngzCmBgYAoKYGBge3J9CiMgU2F2aW5nIFIgZGF0YSBodHRwOi8vdGhvbWFzbGVlcGVyLmNvbS9SY291cnNlL1R1dG9yaWFscy9zYXZpbmdkYXRhLmh0bWwgCnNldC5zZWVkKDEpCm15ZGYgPC0gZGF0YS5mcmFtZSh4ID0gcm5vcm0oMTApLCB5ID0gcm5vcm0oMTApLCB6ID0gcm5vcm0oMTApKQpgYGAKCmBgYHtyfQpzYXZlKG15ZGYsIGZpbGUgPSAic2F2ZWRkZi5SRGF0YSIpICMgY2FuIGJlIGxvYWRlZCBieSBkb3VibGUtY2xpY2tpbmcgb24gc2F2ZWQgZmlsZQpgYGAKCmBgYHtyfQp1bmxpbmsoInNhdmVkZGYuUkRhdGEiKSAjIHJlbW92aW5nIGZpbGUKYGBgCgpgYGB7cn0KIyBkcHV0IHRvIGhhdmUgYSByZWFkYWJsZSBmb3JtYXQgKGUuZy4gZm9yIHN0YWNrIG92ZXJmbG93KQpkcHV0KG15ZGYpCmBgYAoKYGBge3J9CmRwdXQobXlkZiwgInNhdmVkZGYudHh0IikKYGBgCgpgYGB7cn0KbXlkZjIgPC0gZGdldCgic2F2ZWRkZi50eHQiKQpteWRmMgpgYGAKCmBgYHtyfQpteWRmPT1teWRmMiAjIGR1ZSB0byByb3VuZGluZwpgYGAKCmBgYHtyfQp1bmxpbmsoInNhdmVkZGYudGV4dCIpCmBgYAoKYGBge3J9CiMgY3N2CndyaXRlLmNzdihteWRmLCBmaWxlID0gInNhdmVkZGYuY3N2IikKdW5saW5rKCJzYXZlZGYuY3N2IikKYGBgCgpgYGB7cn0KIyBEYXRhZnJhbWUgcmVhcnJhbmdlbWVudAoKc2V0LnNlZWQoNTApCm15ZGYgPC0gZGF0YS5mcmFtZShhID0gcmVwKDE6MiwgZWFjaCA9IDEwKSwgYiA9IHJlcCgxOjQsIHRpbWVzID0gNSksIGMgPSBybm9ybSgyMCksIAogICAgZCA9IHJub3JtKDIwKSwgZSA9IHNhbXBsZSgxOjIwLCAyMCwgRkFMU0UpKQpoZWFkKG15ZGYpCmBgYAoKYGBge3J9CiMgbWFudWFsIG9yZGVyIGNoYW5nZQoKaGVhZChteWRmWywgYygiYyIsICJkIiwgImUiLCAiYSIsICJiIildKQojIG15ZGYgPC0gbXlkZlssIGMoMywgNCwgNSwgMSwgMildCmBgYAoKYGBge3J9CiMgdXNpbmcgb3JkZXIKb3JkZXIobXlkZiRlKQpoZWFkKG15ZGZbb3JkZXIobXlkZiRlKSwgXSkgIyBvcmRlcmluZyBvbiBhbnkgY29sdW1uCmBgYAoKYGBge3J9CiMgU3Vic2V0CgpteWRmW215ZGYkYSA9PSAxLCBdCm15ZGZbbXlkZiRhID09IDEgJiBteWRmJGIgPiAyLCBdCgpzdWJzZXQobXlkZiwgYSA9PSAxICYgYiA+IDIpCnN1YnNldChteWRmLCBzZWxlY3QgPSBjKCJhIiwgImIiKSkKc3Vic2V0KG15ZGYsIGEgPT0gMSAmIGIgPiAyLCBzZWxlY3QgPSBjKCJjIiwgImQiKSkgIyBmaWx0ZXIgcm93cyAmIGNvbHVtbnMKCmBgYAoKYGBge3J9CiMgU3BsaXR0aW5nIAoKc3BsaXQobXlkZiwgbXlkZiRhKSAjIC0+IHNwbGl0dGluZyBhY2NvcmRpbmcgdG8gdmFsdWVzIG9mIGEKYGBgCgpgYGB7cn0Kc3BsaXQobXlkZiwgbGlzdChteWRmJGEsIG15ZGYkYikpCmBgYAoKYGBge3J9CmxhcHBseShzcGxpdChteWRmLCBteWRmJGEpLCBzdW1tYXJ5KSAjIHBlcmZvcm0gc3VtbWFyeSBvbiBlYWNoIG9mIHRoZSBkYXRhZnJhbWVzCmBgYAoKYGBge3J9CiMgc2FtcGxpbmc6IHNwbGl0dGluZyBpbnRvIHRyYWluaW5nIGFuZCB0ZXN0IHNldAojIE9wdGlvbiAxCnMgPC0gc2FtcGxlKDE6bnJvdyhteWRmKSwgNSwgRikgI25vIHJlcGxhY2VtZW50CnMKYGBgCgpgYGB7cn0KIyB1c2UgNSByb3dzIGFzIHRyYWluaW5nIHNldApteWRmW3MsXQpgYGAKCmBgYHtyfQojIHRlc3Qgc2V0Cm15ZGZbLXMsIF0KYGBgCgpgYGB7cn0KIyBPcHRpb24gMgpzMiA8LSByYmlub20obnJvdyhteWRmKSwgMSwgMC4yKSAjd29uJ3QgbmVjZXNzYXJpbHkgZ2l2ZSBleGFjdGx5IDUgcm93cwpzMgpgYGAKCmBgYHtyfQpteWRmW3MyLF0KbXlkZlstczIsXQpgYGAKCmBgYHtyfQojIFJlY29kaW5nIHZlY3RvcnMKbGlicmFyeShjYXIpCmBgYAoKYGBge3J9CmIgPC0gMToyMAojaCA8LSByZWNvZGUoYiwgIjE6NT0xOiA2OjEwPTI7IGVsc2U9TkEiKSAjIGluY3JlZGlibHkgdGhpcyBjcmVhdGVzIGFuIGVycm9yCmUgPC0gcmVjb2RlKGIsICIxOjU9MTsgNjoxMD0yOyBlbHNlPU5BIikKZQpmIDwtIHJlY29kZShiLCAibG86NT0xOyA2OjEwPTI7IDExOjE1PTM7IDE2OmhpPTQ7IGVsc2U9TkEiKQpmCmBgYAoKYGBge3J9CmUgPC0gcmVjb2RlKGgsICJOQT05OSIpCmUKYGBgCgpgYGB7cn0KIyBSZWNvbmRpbmcgb24gbXVsdGlwbGUgdmFyaWFibGVzCmkgPC0gZXhwYW5kLmdyaWQoMTo0LCAxOjIpCmkKYGBgCgpgYGB7cn0KaW50ZXJhY3Rpb24oaSRWYXIxLCBpJFZhcjIpICMgY3JlYXRlcyBhbGwgcG9zc2libGUgY29tYmluYXRpb25zCmBgYAoKYGBge3J9CiMgU2NhbGluZwpzZXQuc2VlZCgxKQpuIDwtIDMwCm15ZGYgPC0gZGF0YS5mcmFtZSh4MSA9IHJiaW5vbShuLCAxLCAwLjUpLCB4MiA9IHJiaW5vbShuLCAxLCAwLjEpLCB4MyA9IHJiaW5vbShuLCAKICAgIDEsIDAuNSksIHg0ID0gcmJpbm9tKG4sIDEsIDAuOCksIHg1ID0gMSwgeDYgPSBzYW1wbGUoYygwLCAxLCBOQSksIG4sIFRSVUUpKQpgYGAKCmBgYHtyfQpzdHIobXlkZikKYGBgCgpgYGB7cn0KbXlkZiR4MSArIG15ZGYkeDIgLSBteWRmJHgzICMgdmVjdG9yIG9wZXJhdGlvbnMKd2l0aChteWRmLCB4MSt4Mi14MykgIyB3aXRoIHRvIGluZGljYXRlIGRhdGFmcmFtZQoKYGBgCgpgYGB7cn0Kcm93U3VtcyhteWRmKQpyb3dTdW1zKG15ZGYsIG5hLnJtID0gVCkKZGF0YS5mcmFtZSgxOm4sIHJvd1N1bXMobXlkZiwgbmEucm0gPSBUKSkKYGBgCgpgYGB7cn0Kcm93TWVhbnMobXlkZikKYGBgCgpgYGB7cn0KYXBwbHkobXlkZiwgMSwgdmFyKSAjIDJuZCBhcmd1bWVudDogMSBmb3Igcm93cywgMiBmb3IgY29sdW1ucywgYygxLCAyKSAgcm93cyAmIGNvbHVtbnMuCmFwcGx5KG15ZGYsIDIsIHZhcikKc2FwcGx5KG15ZGYsIHZhcikgIyBvdmVyIGxpc3Qgb3IgdmVjdG9yCmBgYAoKYGBge3J9CiMgYWRkaW5nIGEgdmFyaWFibGUKbmV3dmFyIDwtIG51bWVyaWMobnJvdyhteWRmKSkKbmV3dmFyW215ZGYkeDEgPT0gMV0gPC0gd2l0aChteWRmW215ZGYkeDEgPT0gMSwgXSwgeDIgKyB4MykKbmV3dmFyW215ZGYkeDEgPT0gMF0gPC0gd2l0aChteWRmW215ZGYkeDEgPT0gMCwgXSwgeDMgKyB4NCArIHg1KQpuZXd2YXIKYGBgCgpgYGB7cn0KbmV3dmFyW215ZGYkeDEgPT0gMV0gPC0gd2l0aChteWRmLCB4MiArIHgzKSAjIGhlcmUgZGlmZmVyZW50IGxlbmd0aHMgIQpgYGAKCmBgYHtyfQojIE1hdHJpY2VzCnNldC5zZWVkKDEpCmEgPC0gcm5vcm0oMTAwKQpxdWFudGlsZShhLCBjKDAuMDI1LCAwLjk3NSkpCnF1YW50aWxlKGEsIHNlcSgwLCAxLCBieSA9IDAuMSkpCmBgYAoKYGBge3J9CnN1bW1hcnkoYXMubG9naWNhbChyYmlub20oMTAwMCwgMSwgMC41KSkpCmBgYAoKYGBge3J9CnN1bW1hcnkoZmFjdG9yKGEpKSAjIGZvciBmYWN0b3IsIHJldHVybnMgYWxsIHZhbHVlCmBgYAoKYGBge3J9CiMgVGFibGVzCnNldC5zZWVkKDEpCmEgPC0gc2FtcGxlKDE6NSwgMjUsIFQpCmEKYGBgCgpgYGB7cn0KdGFibGUoYSkKYGBgCgpgYGB7cn0KcHJvcC50YWJsZSh0YWJsZShhKSkgIyB0byBvYnRhaW4gcGVyY2VudGFnZXMKcHJvcC50YWJsZSh0YWJsZShhKSkgKjEwMApgYGAKCmBgYHtyfQpjYmluZCh0YWJsZShhKSwgcHJvcC50YWJsZSh0YWJsZShhKSkqMTAwKQpgYGAKCmBgYHtyfQojIG11bHRpLXZhcmlhdGUKYiA8LSByZXAoYygxLCAyKSwgbGVuZ3RoID0gMjUpCnRhYmxlKGEsIGIpCmBgYAoKYGBge3J9CmMgPC0gcmVwKGMoMywgNCwgNSksIGxlbmd0aCA9IDI1KQp0YWJsZShhLCBiLCBjKQpgYGAKCmBgYHtyfQpmdGFibGUoYSwgYywgYykgIyBwcm92aWRlcyBtb3JlIGNvbXBhY3QgZm9ybWF0CmBgYAoKYGBge3J9Cnh0YWJzKH5hICsgYikgIyByaWdodCBoYW5kIGZvcm11bGFzIHNhbWUgYXMgdGFibGUKYGBgCgpgYGB7cn0KeCA8LSB0YWJsZShhLCBiKQphZGRtYXJnaW5zKHgpCmBgYAoKYGBge3J9CnByb3AudGFibGUodGFibGUoYSwgYiksIDEpICMgcHJvcG9ydGlvbnMgYnkgcm93cwpwcm9wLnRhYmxlKHRhYmxlKGEsIGIpLCAyKQpgYGAKCmBgYHtyfQojIENvcnJlbGF0aW9ucwpzZXQuc2VlZCgxKQpuIDwtIDEwMDAKeDEgPC0gcm5vcm0obiwgLTEsIDEwKQp4MiA8LSBybm9ybShuLCAzLCAyKQp5IDwtIDUgKiB4MSArIHgyICsgcm5vcm0obiwgMSwgMikKYGBgCgpgYGB7cn0KY29yKHgxLCB4MikKY29yLnRlc3QoeDEsIHgyKQpgYGAKCmBgYHtyfQpjb3IoY2JpbmQoeDEsIHgyLCB5KSkgIyBpbnB1dCBpcyBtYXRyaXggZm9yIGNvcnJlbGF0aW9uIG1hdHJpeApgYGAKCmBgYHtyfQphIDwtIHJub3JtKG4pCmIgPC0gYV4yICsgcm5vcm0obikKcGxvdChifmEpCmBgYAoKYGBge3J9CnBsb3QoYiB+IGEsIGNvbCA9ICJncmF5IikKY3VydmUoKHgpLCBjb2wgPSAicmVkIiwgYWRkID0gVFJVRSkKY3VydmUoKHheMiksIGNvbCA9ICJibHVlIiwgYWRkID0gVFJVRSkKYGBgCgpgYGB7cn0KY29yKGEsIGIpCmNvcihhXjIsIGIpCmBgYAoKYGBge3J9CnBsb3QoYn5JKGFeMiksIGNvbCA9ICJvcmFuZ2UiKQphYmxpbmUobG0oYn5JKGFeMikpLCBjb2wgPSAicmVkIikKYGBgCgpgYGB7cn0KbGF5b3V0KG1hdHJpeCgxOjIsIG5yb3cgPSAxKSkKcGxvdChiIH4gYSwgY29sID0gImdyYXkiKQpjdXJ2ZSgoeF4yKSwgY29sID0gImJsdWUiLCBhZGQgPSBUUlVFKQpwbG90KGIgfiBJKGFeMiksIGNvbCA9ICJncmF5IikKY3VydmUoKHgpLCBjb2wgPSAiYmx1ZSIsIGFkZCA9IFRSVUUpCmBgYAoKYGBge3J9CiMgUm91bmRpbmcKaGVpZ2h0IDwtIGMoMTY3LCAxNjQsIDE3MiwgMTU4LCAxODEsIDE3OSkKbWVhbihoZWlnaHQpCmBgYAoKYGBge3J9CnNpZ25pZihtZWFuKGhlaWdodCksIDQpCnJvdW5kKG1lYW4oaGVpZ2h0KSwgMSkKcm91bmQobWVhbihoZWlnaHQpLCAtMikKYGBgCgpgYGB7cn0Kb3B0aW9ucyhkaWdpdHMgPSA1KQpzZChoZWlnaHQpCm9wdGlvbnMoZGlnaXRzID0gMikKc2QoaGVpZ2h0KQpgYGAKCmBgYHtyfQpvcHRpb25zKHNjaXBlbiA9IC0xMCkgI3Bvc2l0aXZlIHZhbHVlIHRvIGdldCBmaXhlZCBub3RhdGlvbgoxMDAwMDAwMApgYGAKCmBgYHtyfQojIHNwcmludGYKc3ByaW50ZigiJS4zZiIsIHBpKQpzcHJpbnRmKCIlMDUuMWYiLCBwaSkKYGBgCgpgYGB7cn0KIyBQbG90cyBhcyBkYXRhIHN1bW1hcnkKc2V0LnNlZWQoMSkKYSA8LSBybm9ybSgzMCkKaGlzdChhLCBjb2wgPSAiZ3JheTIwIiwgYm9yZGVyID0gImxpZ2h0Z3JheSIpCmBgYAoKYGBge3J9CmRlbnNpdHkoYSkKcGxvdChkZW5zaXR5KGEpKQpgYGAKCmBgYHtyfQpoaXN0KGEsIGZyZXEgPSBGQUxTRSwgY29sID0gImdyYXkyMCIsIGJvcmRlciA9ICJsaWdodGdyYXkiKQpsaW5lcyhkZW5zaXR5KGEpLCBjb2wgPSAicmVkIiwgbHdkID0gMikKYGBgCgpgYGB7cn0KYiA8LSBjKDMsIDQuNSwgNSwgOCwgMywgNikKYmFycGxvdChiLCBuYW1lcy5hcmcgPSBsZXR0ZXJzWzE6bGVuZ3RoKGIpXSwgaG9yaXogPSBGKQpgYGAKCmBgYHtyfQpkIDwtIHJiaW5kKGMoMiwgNCwgMSksIGMoNiwgMSwgMykpCmQKYmFycGxvdChkLCBuYW1lcy5hcmcgPSBsZXR0ZXJzWzE6M10pCmBgYAoKYGBge3J9CmJhcnBsb3QoZCwgYmVzaWRlID0gVCkKYGBgCgpgYGB7cn0KbGF5b3V0KG1hdHJpeCgxOjIsIG5yb3cgPSAxKSkKYmFycGxvdChiLCBuYW1lcy5hcmcgPSBsZXR0ZXJzWzE6Nl0sIGhvcml6ID0gVFJVRSwgbGFzID0gMikKZG90Y2hhcnQoYiwgbGFiZWxzID0gbGV0dGVyc1sxOjZdLCB4bGltID0gYygwLCA4KSkKYGBgCgpgYGB7cn0KYm94cGxvdChhKQpgYGAKCmBgYHtyfQplIDwtIHJub3JtKDEwMCwgMSwgMSkKZiA8LSBybm9ybSgxMDAsIDIsIDQpCmJveHBsb3QoZSwgZikKYGBgCgpgYGB7cn0KZzEgPC0gYyhlLCBmKQpnMiA8LSByZXAoYygxLCAyKSwgZWFjaCA9IDEwMCkKYm94cGxvdChnMSB+IGcyKQpgYGAKCmBgYHtyfQojIFNjYXR0ZXJwbG90CngxIDwtIHJub3JtKDEwMDApCngyIDwtIHJub3JtKDEwMDApCngzIDwtIHgxICsgeDIKeDQgPC0geDEgKyB4MwpgYGAKCmBgYHtyfQpwbG90KHgxLCB4MikKcGxvdCh4Mn54MSkKYGBgCgpgYGB7cn0KbGF5b3V0KG1hdHJpeCgxOjMsIG5yb3cgPSAxKSkKcGxvdCh4MSwgeDIpCnBsb3QoeDEsIHgzKQpwbG90KHgxLCB4NCkKYGBgCgpgYGB7cn0KcGFpcnMofngxICsgeDIgKyB4MyArIHg0KQpgYGAKCmBgYHtyfQpjb2xvcnMoKVsxOjEwXQpsZW5ndGgoY29sb3JzKCkpCmNvbG9ycygpWzYwMF0KYGBgCgpgYGB7cn0Kc2V0LnNlZWQoMTAwKQp6IDwtIHNhbXBsZSgxOjQsIDEwMCwgVFJVRSkKeCA8LSBybm9ybSgxMDApCnkgPC0gcm5vcm0oMTAwKQpwbG90KHgsIHksIHBjaCA9IDE1LCBjb2wgPSBjKCJyZWQiLCAiYmx1ZSIpKQpgYGAKCmBgYHtyfQpjKCJyZWQiLCAiYmx1ZSIsICJncmVlbiIsICJvcmFuZ2UiKVt6XQpwbG90KHgsIHksIHBjaCA9IDE1LCBjb2wgPSBjKCJyZWQiLCAiYmx1ZSIsICJncmVlbiIsICJvcmFuZ2UiKVt6XSkgI2luZGV4aW5nIGNvbG9ycyBvbiB6IGdyb3VwcwpgYGAKCmBgYHtyfQojIEFuYWx5c2lzIG9mIHZhcmlhbmNlIAoKc2V0LnNlZWQoMTAwKQp0ciA8LSByZXAoMTo0LCBlYWNoID0gMzApCnkgPC0gbnVtZXJpYyhsZW5ndGggPSAxMjApCnlbdHIgPT0gMV0gPC0gcm5vcm0oMzAsIDUsIDEpCnlbdHIgPT0gMl0gPC0gcm5vcm0oMzAsIDQsIDIpCnlbdHIgPT0gM10gPC0gcm5vcm0oMzAsIDQsIDUpCnlbdHIgPT0gNF0gPC0gcm5vcm0oMzAsIDEsIDIpCmBgYAoKYGBge3J9CmFvdih5fnRyKQpgYGAKCmBgYHtyfQpzdW1tYXJ5KGFvdih5IH4gZmFjdG9yKHRyKSkpCmBgYAoKYGBge3J9Cm9uZXdheS50ZXN0KHkgfiB0cikKYGBgCgpgYGB7cn0Kb25ld2F5LnRlc3QoeSB+IGZhY3Rvcih0ciksIHZhci5lcXVhbCA9IFRSVUUpCmBgYAoKYGBge3J9CmJ5KHksIHRyLCBGVU4gPSBtZWFuKQpgYGAKCmBgYHtyfQp0YXBwbHkoeSwgdHIsIEZVTiA9IG1lYW4pICMgc2FtZSB0aGluZwpgYGAKCmBgYHtyfQojIERpc3RyaWJ1dGlvbnMKb3B0aW9ucyhzY2lwZW4gPSBGKQpvcHRpb25zKGRpZ2l0cyA9IDUpCmRub3JtKDApICAjIGRlbnNpdHkKZG5vcm0oMCwgbWVhbj0tMSkKYGBgCgpgYGB7cn0KcG5vcm0oMCkgICMgY3VtdWxhdGl2ZQpwbm9ybSgxLjY1KSAjIDk1JSBub3JtYWwgY29uZmlkZW5jZSBpbnRlcnZhbApwbm9ybSgxLjk2KQpwbm9ybSgxLjk2KSAtIHBub3JtKC0xLjk2KQpgYGAKCmBgYHtyfQpxbm9ybShjKDAuMDI1LCAwLjk3NSkpICAjIHF1YW50aWxlCnBub3JtKHFub3JtKGMoMC4wMjUsIDAuOTc1KSkpCmBgYAoKYGBge3J9CiMgb3RoZXIgZGlzdHJpYnV0aW9uCmRiaW5vbSgwLCAxLCAwLjUpCnBiaW5vbSgwLCAxLCAwLjUpCnFiaW5vbSguOTUsIDEsIDAuNSkgIyBiZWNhdXNlIGJpbm9taWFsIGRpc2NyZXRlIGRpc3RyaWJ1dGlvbgpgYGAKCmBgYHtyfQojIEZvcm11bGFlCm15Zm9ybXVsYSA8LSB+eApjbGFzcyhteWZvcm11bGEpCmBgYAoKYGBge3J9CiMgaW50ZXJhY3Rpb25zCnkgfiB4MSAqIHgyCnkgfiB4MTp4MiAjIHdpdGhvdXQgdGhlIHZhcmlhYmxlcyB0aGVtc2VsdmVzIAp5IH4gLTEgKyB4MSAqIHgyICMgZHJvcCB0aGUgaW50ZXJjZXB0CnkgfiB4ICsgSSh4XjIpICMgd2l0aG91dCBJKCkgUiB0aGlua3MgeF4yIGlzIGEgZHVwbGljYXRlCmBgYAoKYGBge3J9CiMgQXMgc3RyaW5ncwooInkgfiB4IikgPT0gKHkgfiB4KQphcy5mb3JtdWxhKCJ5fngiKQphcy5jaGFyYWN0ZXIoeSB+IHgpICMgRm9ybXVsYSBpbmRleGVkIHdpdGggb3BlcmFuZCBmaXJzdApgYGAKCmBgYHtyfQp0ZXJtcyh5IH4geDEgKyB4MikKYGBgCgpgYGB7cn0KdXBkYXRlKHkgfiB4LCB+LiArIHgyKQp1cGRhdGUoeSB+IHgsIHogfiAuKQpgYGAKCmBgYHtyfQojIEJpdmFyaWF0ZSBSZWdyZXNzaW9uCgpzZXQuc2VlZCgxKQpiaW4gPC0gcmJpbm9tKDEwMDAsIDEsIDAuNSkKCm91dCA8LSAyICogYmluICsgcm5vcm0oMTAwMCkKCmJ5KG91dCwgYmluLCBtZWFuKQpgYGAKCmBgYHtyfQp0LnRlc3Qob3V0IH4gYmluKQpgYGAKCmBgYHtyfQpsbShvdXQgfiBiaW4pICMgc2xvcGUgPSBtZWFuIGRpZmZlcmVuY2UKYGBgCgpgYGB7cn0Kc3VtbWFyeShsbShvdXR+YmluKSkkY29lZgpgYGAKCmBgYHtyfQpwbG90KG91dCB+IGJpbiwgY29sID0gImdyYXkiKQpwb2ludHMoMDoxLCBieShvdXQsIGJpbiwgbWVhbiksIGNvbCA9ICJibHVlIiwgYmcgPSAiYmx1ZSIsIHBjaCA9IDIzKQphYmxpbmUoY29lZihsbShvdXQgfiBiaW4pKSwgY29sID0gImJsdWUiKQpgYGAKCmBgYHtyfQpzZXQuc2VlZCgxKQp4IDwtIHJ1bmlmKDEwMDAsIDAsIDEwKQp5IDwtIDMgKiB4ICsgcm5vcm0oMTAwMCwgMCwgNSkKYGBgCgpgYGB7cn0KIyBnbG0gcGxvdHMKc2V0LnNlZWQoMSkKbiA8LSAxMDAKeCA8LSBydW5pZihuLCAwLCAxKQp5IDwtIHJiaW5vbShuLCAxLCB4KSAjIG1vcmUgb3V0Y29tZXMgb2YgMSBhcyB4IC0+IDEKYGBgCgpgYGB7cn0KcGxvdCh5IH4geCwgY29sID0gTlVMTCwgYmcgPSByZ2IoMCwgMCwgMCwgMC41KSwgcGNoID0gMjEpICMgYmc6IGJhY2tncm91bmQgY29sb3IgKGZvciBwb2ludHMpCmFibGluZShsbSh5IH4geCksIGx3ZCA9IDIpICMgbHdkOiBsaW5lIHdpZHRoIChkZWZhdWx0OiAxKQojIGhlcmUgbGluZWFyIGRvZXNuJ3Qgd29yawpgYGAKCmBgYHtyfQptMSA8LSBnbG0oeSB+IHgsIGZhbWlseSA9IGJpbm9taWFsKGxpbmsgPSAibG9naXQiKSkKYGBgCgpgYGB7cn0KbmV3ZGYgPC0gZGF0YS5mcmFtZSh4ID0gc2VxKDAsIDEsIGxlbmd0aC5vdXQgPSAxMDApKQpuZXdkZgpgYGAKCmBgYHtyfQpuZXdkZiRwb3V0X2xvZ2l0IDwtIHByZWRpY3QobTEsIG5ld2RmLCBzZS5maXQgPSBUUlVFLCB0eXBlID0gInJlc3BvbnNlIikkZml0Cm5ld2RmWzk1OjEwMCxdCmBgYAoKYGBge3J9CiMgYnVpbGQgY29uZmlkZW5jZSBpbnRlcnZhbHMgZnJvbSBzdGFuZGFyZCBlcnJvcgpuZXdkZiRwc2VfbG9naXQgPC0gcHJlZGljdChtMSwgbmV3ZGYsIHNlLmZpdCA9IFRSVUUsIHR5cGUgPSAicmVzcG9uc2UiKSRzZS5maXQKbmV3ZGYkcGxvd2VyX2xvZ2l0IDwtIG5ld2RmJHBvdXRfbG9naXQgLSAoMS45NiAqIG5ld2RmJHBzZV9sb2dpdCkgICMgOTUlIENJIGxvd2VyIGJvdW5kCm5ld2RmJHB1cHBlcl9sb2dpdCA8LSBuZXdkZiRwb3V0X2xvZ2l0ICsgKDEuOTYgKiBuZXdkZiRwc2VfbG9naXQpICAjIDk1JSBDSSB1cHBlciBib3VuZAojIHFub3JtKGMoMC4wMjUsIDAuOTc1KSkgPSAoLTEuOTYsICsxLjk2KQpgYGAKCmBgYHtyfQpuZXdkZlsxOjEwLGMoMSwyLDMsNSw0KV0KYGBgCgpgYGB7cn0KIyBub3cgcGxvdCBwcmVkaWN0ZWQgdmFsdWVzCndpdGgobmV3ZGYsIHBsb3QocG91dF9sb2dpdCB+IHgsIHR5cGUgPSAibCIsIGx3ZCA9IDIpKQp3aXRoKG5ld2RmLCBsaW5lcyhwdXBwZXJfbG9naXQgfiB4LCB0eXBlID0gImwiLCBsdHkgPSAyKSkKd2l0aChuZXdkZiwgbGluZXMocGxvd2VyX2xvZ2l0IH4geCwgdHlwZSA9ICJsIiwgbHR5ID0gMikpCmBgYAoKYGBge3J9CmxpYnJhcnkoZ2dwbG90MikKIyBodHRwOi8vZ2dwbG90Mi50aWR5dmVyc2Uub3JnL3JlZmVyZW5jZS8KCiMgYmFzaWMgUidzIHBsb3QKcGxvdChpcmlzJFNlcGFsLldpZHRoLCBpcmlzJFNlcGFsLkxlbmd0aCkKYGBgCgpgYGB7cn0KaGVhZChtcGcpCmdncGxvdChkYXRhID0gbXBnLCBhZXMoeCA9IGRpc3BsLCB5ID0gaHd5KSkgKyBnZW9tX3BvaW50KCkKZ2dwbG90KGRhdGEgPSBtcGcsIGFlcyh4ID0gZGlzcGwsIHkgPSBod3kpKSArIGdlb21fcG9pbnQoKSArIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIpICMgd2l0aCByZWdyZXNzaW9uCmBgYAoKYGBge3J9CmdncGxvdChkYXRhID0gbXBnLCBhZXMoeCA9IGRpc3BsLCB5ID0gaHd5KSkgKyBnZW9tX3BvaW50KGFlcyhjb2xvciA9IGNsYXNzKSkgIyBhZGRpbmcgYSBkaW1lbnNpb24KZ2dwbG90KGRhdGEgPSBtcGcsIGFlcyh4ID0gZGlzcGwsIHkgPSBod3kpKSArIGdlb21fcG9pbnQoYWVzKHNpemUgPSBjbGFzcykpCmdncGxvdChkYXRhID0gbXBnLCBhZXMoeCA9IGRpc3BsLCB5ID0gaHd5KSkgKyBnZW9tX3BvaW50KGFlcyhzaGFwZSA9IGNsYXNzKSkKZ2dwbG90KGRhdGEgPSBtcGcsIGFlcyh4ID0gZGlzcGwsIHkgPSBod3kpKSArIGdlb21fcG9pbnQoYWVzKGFscGhhID0gY2xhc3MpKQpgYGAKCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IG1wZywgYWVzKHggPSBkaXNwbCwgeSA9IGh3eSkpICsgZ2VvbV9wb2ludCgpICsgZmFjZXRfZ3JpZCguIH4gY3lsKSAjIDJEIGdyaWQsIHJvd3MgfiBjb2xzCmdncGxvdChkYXRhID0gbXBnLCBhZXMoeCA9IGRpc3BsLCB5ID0gaHd5KSkgKyBnZW9tX3BvaW50KCkgKyBmYWNldF9ncmlkKGRydiB+IC4pIApnZ3Bsb3QoZGF0YSA9IG1wZywgYWVzKHggPSBkaXNwbCwgeSA9IGh3eSkpICsgZ2VvbV9wb2ludCgpICsgZmFjZXRfZ3JpZChkcnYgfiBjeWwpCmdncGxvdChkYXRhID0gbXBnLCBhZXMoeCA9IGRpc3BsLCB5ID0gaHd5KSkgKyBnZW9tX3BvaW50KCkgKyBmYWNldF93cmFwKCB+IGNsYXNzKQojIGZhY2V0X3dyYXAgd3JhcHMgYSAxZCBzZXF1ZW5jZSBvZiBwYW5lbHMgaW50byAyZC4gVGhpcyBpcyBnZW5lcmFsbHkgYSBiZXR0ZXIgdXNlIG9mIHNjcmVlbiBzcGFjZSB0aGFuIGZhY2V0X2dyaWQgYmVjYXVzZSBtb3N0IGRpc3BsYXlzIGFyZSByb3VnaGx5IHJlY3Rhbmd1bGFyLgpgYGAKCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IG1wZywgYWVzKHggPSBkaXNwbCwgeSA9IGh3eSkpICsgIGdlb21fcG9pbnQoKSArIGdlb21fc21vb3RoKCkKZ2dwbG90KGRhdGEgPSBtcGcsIGFlcyh4ID0gZGlzcGwsIHkgPSBod3kpKSArIGdlb21fcG9pbnQoKSArIGdlb21fc21vb3RoKHNlID0gRkFMU0UpICAjIFR1cm4gb2ZmIGNvbmZpZGVuY2UgYmFuZApgYGAKCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IG1wZywgYWVzKHggPSBjbGFzcywgeSA9IGh3eSkpICsgZ2VvbV9ib3hwbG90KCkgIyBzY2F0dGVycGxvdApnZ3Bsb3QoZGF0YSA9IG1wZywgYWVzKHggPSByZW9yZGVyKGNsYXNzLCBod3kpLCB5ID0gaHd5KSkgKyBnZW9tX2JveHBsb3QoKSAjIHJlb3JkZXIgKG1lYW4pCmdncGxvdChkYXRhID0gbXBnLCBhZXMoeCA9IHJlb3JkZXIoY2xhc3MsIGh3eSwgbWVkaWFuKSwgeSA9IGh3eSkpICsgZ2VvbV9ib3hwbG90KCkgIyByZW9yZGVyIGJ5IG1lZGlhbgpgYGAKYGBge3J9CiMgaml0dGVyCmdncGxvdChkYXRhID0gbXBnLCBhZXMoeCA9IGN0eSwgeSA9IGh3eSkpICsgZ2VvbV9wb2ludCgpCmdncGxvdChkYXRhID0gbXBnLCBhZXMoeCA9IGN0eSwgeSA9IGh3eSkpICsgZ2VvbV9wb2ludChwb3NpdGlvbiA9ICJqaXR0ZXIiKQpnZ3Bsb3QoZGF0YSA9IG1wZywgYWVzKHggPSBjdHksIHkgPSBod3kpKSArIGdlb21faml0dGVyKCkgIyBpZGVudGljYWwgb3B0aW9uCmBgYApgYGB7cn0KbmFtZXMoZGlhbW9uZHMpICMgcGFydCBvZiBnZ3Bsb3QyIHBhY2thZ2UKZ2dwbG90KGRhdGEgPSBkaWFtb25kcyxhZXMoeCA9Y3V0KSkgKyBnZW9tX2JhcihhZXMoZmlsbCA9Y3V0KSkgIyBmaWxsOiBjb2xvciBpbnNpZGUgb2YgYmFycwpnZ3Bsb3QoZGF0YSA9IGRpYW1vbmRzLCBhZXMoeCA9Y3V0KSkgKyBnZW9tX2JhcihhZXMoY29sb3IgPWN1dCkpICMgY29sb3I6IGxpbmUgYXJvdW5kIHRoZSBiYXJzCmdncGxvdChkYXRhID0gZGlhbW9uZHMsIGFlcyh4ID0gY29sb3IpKSArIGdlb21fYmFyKGFlcyhmaWxsID0gY3V0KSwgcG9zaXRpb24gPSAiZG9kZ2UiKQpgYGAKCgoKYGBge3J9CnN0cihkaWFtb25kcykKZ2dwbG90KGRhdGEgPSBkaWFtb25kcywgYWVzKHggPSBjYXJhdCkpICsgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAxKQpnZ3Bsb3QoZGF0YSA9IGRpYW1vbmRzLCBhZXMoeCA9IGNhcmF0KSkgKyBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDAuMDEpCmdncGxvdChkYXRhID0gZGlhbW9uZHMsIGFlcyh4ID0gY2FyYXQpKSArIGdlb21faGlzdG9ncmFtKCkgI3N0YXRfYmluOiBiaW53aWR0aCBkZWZhdWx0ZWQgdG8gcmFuZ2UvMzAuCmBgYAoKYGBge3J9Cnpvb20gPC0gY29vcmRfY2FydGVzaWFuKHhsaW0gPSBjKDU1LCA3MCkpCmdncGxvdChkYXRhID0gZGlhbW9uZHMsIGFlcyh4ID0gZGVwdGgpKSArIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gMC4yKSArIHpvb20KYGBgCgpgYGB7cn0KZ2dwbG90KGRhdGEgPSBkaWFtb25kcywgYWVzKHggPSBkZXB0aCkpICsgZ2VvbV9oaXN0b2dyYW0oYWVzKGZpbGwgPSBjdXQpLCBiaW53aWR0aCA9IDAuMSkgKyB6b29tCmBgYAoKYGBge3J9CiMgdG8gY29tcGFyZSB2YXJpYWJsZXMKZ2dwbG90KGRhdGEgPSBkaWFtb25kcywgYWVzKHggPSBkZXB0aCkpICsgZ2VvbV9kZW5zaXR5KGFlcyhmaWxsID0gY3V0KSkgKyB6b29tCmdncGxvdChkYXRhID0gZGlhbW9uZHMsIGFlcyh4ID0gZGVwdGgpKSArIGdlb21fZGVuc2l0eShhZXMoY29sb3IgPSBjdXQsIGZpbGwgPSBjdXQsIGFscGhhPTAuMSkpICsgem9vbQpgYGAKCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IGRpYW1vbmRzLCBhZXMoeCA9IHByaWNlKSkgK2dlb21faGlzdG9ncmFtKGFlcyhmaWxsID0gY3V0KSwgYmlud2lkdGggPSAxMDApCmdncGxvdChkYXRhID0gZGlhbW9uZHMsIGFlcyh4ID0gcHJpY2UpKSArIGdlb21fZGVuc2l0eShhZXMoY29sb3I9IGN1dCkpICMgYmV0dGVyCmBgYAoKYGBge3J9CiMgVmlzdWFsaXphdGlvbiBvZiBiaWcgZGF0YQpnZ3Bsb3QoZGF0YSA9IGRpYW1vbmRzLCBhZXMoeCA9IGNhcmF0LCB5ID0gcHJpY2UpKSArIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gY3V0KSkgIyBub3QgaGVscGZ1bApnZ3Bsb3QoZGF0YSA9IGRpYW1vbmRzLCBhZXMoeCA9IGNhcmF0LCB5ID0gcHJpY2UpKSArIGdlb21fYmluMmQoKQpnZ3Bsb3QoZGF0YSA9IGRpYW1vbmRzLCBhZXMoeCA9IGNhcmF0LCB5ID0gcHJpY2UpKSArIGdlb21fZGVuc2l0eTJkKCkKZ2dwbG90KGRhdGEgPSBkaWFtb25kcywgYWVzKHggPSBjYXJhdCwgeSA9IHByaWNlKSkgKyBnZW9tX3BvaW50KCkgKyBnZW9tX2RlbnNpdHkyZCgpCmdncGxvdChkYXRhID0gZGlhbW9uZHMsIGFlcyh4ID0gY2FyYXQsIHkgPSBwcmljZSkpICsgZ2VvbV9zbW9vdGgoYWVzKGdyb3VwID0gY3V0KSkKZ2dwbG90KGRhdGEgPSBkaWFtb25kcywgYWVzKHggPSBjYXJhdCwgeSA9IHByaWNlKSkgKyBnZW9tX3Ntb290aChhZXMoY29sb3IgPSBjdXQpLCBtZXRob2QgPSAibG9lc3MiLCBzZT1GKQpgYGAKCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IGRpYW1vbmRzLCBhZXMoeCA9IGNhcmF0LCB5ID0gcHJpY2UpKSArIGdlb21fcG9pbnQoc2l6ZSA9IDAuNSwgYWxwaGEgPSAwLjEpCmBgYAoKYGBge3J9CiMgZ2dzYXZlKCJteS1wbG90LnBkZiIpICMgUERGIG1vcmUgY3Jpc3AKIyBnZ3NhdmUoIm15LXBsb3QucG5nIikKIyBnZ3NhdmUoIm15LXBsb3QucGRmIiwgd2lkdGggPSA2LCBoZWlnaHQgPSA2KQojIGdnc2F2ZSgibXktcGxvdC5wbmciLCB3aWR0aCA9IDYsIGhlaWdodCA9IDYpCmBgYAoKYGBge3J9CiMgTW9yZSBwbG90IGV4YW1wbGVzCmJuYW1lcyA9IHJlYWQuY3N2KCJibmFtZXMuY3N2Iiwgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKSAKYmlydGhzID0gcmVhZC5jc3YoImJpcnRocy5jc3YiLCBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpCmhlYWQoYmlydGhzLCAzKQpoZWFkKGJuYW1lcywgMykKYGBgCgpgYGB7cn0KIyBTaW1wbGUgcGxvdApRdWVudGluIDwtIGJuYW1lc1tibmFtZXMkbmFtZSA9PSAiTGV0aXRpYSIsIF0gCnFwbG90KHllYXIsIHByb3AsIGRhdGEgPSBRdWVudGluLCBnZW9tID0gImxpbmUiKQpgYGAKCmBgYHtyfQojIEludGVyYWN0aW9ucwptaWNoYWVscyA8LSBibmFtZXNbYm5hbWVzJG5hbWUgPT0gIlF1ZW50aW4iIHwgYm5hbWVzJG5hbWUgPT0gIkFsZXhpcyIgfCBibmFtZXMkbmFtZSA9PSAiR2luYSIsIF0KcXBsb3QoeWVhciwgcHJvcCwgZGF0YSA9IG1pY2hhZWxzLCBnZW9tID0gImxpbmUiLCBjb2xvciA9IGludGVyYWN0aW9uKHNleCwgbmFtZSkpCmBgYAoKYGBge3J9CiMgZHBseXIKbGlicmFyeShkcGx5cikKYm5hbWVzID0gdGJsX2RmKGJuYW1lcykgCmJpcnRocyA9IHRibF9kZihiaXJ0aHMpIApjbGFzcyhibmFtZXMpCmBgYAoKYGBge3J9CiMgZmlsdGVyCmZpbHRlcihibmFtZXMsIHNleCA9PSAiZ2lybCIgJiAoeWVhciA9PSAxOTAwIHwgeWVhciA9PSAyMDAwKSkKYGBgCgpgYGB7cn0KIyBTZWxlY3QgY29sdW1ucwpzZWxlY3QoYm5hbWVzLCBzdGFydHNfd2l0aCgic291bmQiKSkKIyBSZW5hbWUgY29sdW1uCnJlbmFtZShpcmlzLCBwZXRhbF9sZW5ndGggPSBQZXRhbC5MZW5ndGgpCmBgYAoKYGBge3J9CiMgc29ydAphcnJhbmdlKGJuYW1lcywgZGVzYyhwcm9wKSlbMyxdCiMgeWVhciB3aGVyZSBRdWVudGluIHdhcyBtb3N0IHBvcHVsYXIKYXJyYW5nZShmaWx0ZXIoYm5hbWVzLCBuYW1lID09ICJRdWVudGluIiksIGRlc2MocHJvcCkpWzEsXQpgYGAKCmBgYHtyfQojIGFkZCBjb2x1bW5zCm11dGF0ZShiaXJ0aHMsIGRvdWJsZSA9IDIgKiBiaXJ0aHMpCiMgdHJhbnNtdXRlIHRvIGRlbGV0ZSBvbGQgY29sdW1ucwpgYGAKCmBgYHtyfQojIFN1bW1hcml6ZQpzdW1tYXJpc2Uodml2aWFuLG1pbiA9IG1pbihwcm9wKSxtZWFuID0gbWVhbihwcm9wKSwgbWF4ID0gbWF4KHByb3ApLCBudW1iZXIgPSBuKCksIG51bWJlcl9kaXN0aW5jdCA9IG5fZGlzdGluY3QocHJvcCkpCmBgYAoKYGBge3J9CgpibmFtZXMyID0gbGVmdF9qb2luKGJuYW1lcywgYmlydGhzLCBieSA9IGMoInllYXIiLCJzZXgiKSkKYm5hbWVzMiA9IG11dGF0ZShibmFtZXMyLCBuID0gcm91bmQocHJvcCAqIGJpcnRocykpCiMgR3JvdXAKYnlfbmFtZSA9IGdyb3VwX2J5KGJuYW1lczIsIG5hbWUpCmJ5X25hbWUKdG90YWxzID0gc3VtbWFyaXNlKGJ5X25hbWUsIHRvdGFsID0gc3VtKG4pKSAKdG90YWxzCmBgYAoKYGBge3J9CmJuYW1lczIKbmFtZV9zZXggPSBncm91cF9ieShibmFtZXMyLCBuYW1lLCB5ZWFyKSAKdG90YWxzMiA9IHN1bW1hcmlzZShuYW1lX3NleCwgdG90YWwgPSBzdW0obikpIApoZWFkKHRvdGFsczIpCmBgYAoKYGBge3J9CmFycmFuZ2UoYm5hbWVzMiwgbmFtZSlbMTozLF0KYGBgCgpgYGB7cn0KdW5ncm91cChieV9uYW1lKQpgYGAKCmBgYHtyfQojIG90aGVyIGV4YW1wbGUKeWVhcl9zZXggPSBncm91cF9ieShibmFtZXMyLCB5ZWFyLCBzZXgpIAp5dG90YWxzID0gc3VtbWFyaXNlKHllYXJfc2V4LCBiaXJ0aHMgPSBzdW0obikpIAp5dG90YWxzCmBgYAoKYGBge3J9CmJuYW1lczIKYGBgCgpgYGB7cn0KIyBJU0xSIAojIENoYXAgNiBSaWRnZSByZWdyZXNzaW9uIC8gTGFzc28KIyBFeCA4CnNldC5zZWVkKDEpClggPSBybm9ybSgxMDApCmVwcyA9IHJub3JtKDEwMCkKCmJldGEwID0gMwpiZXRhMSA9IDIKYmV0YTIgPSAtMwpiZXRhMyA9IDAuMwpZID0gYmV0YTAgKyBiZXRhMSAqIFggKyBiZXRhMiAqIFheMiArIGJldGEzICogWF4zICsgZXBzCgojIFVzZSByZWdzdWJzZXRzIHRvIHNlbGVjdCBiZXN0IG1vZGVsIGhhdmluZyBwb2x5bm9taWFsIG9mIFhYIG9mIGRlZ3JlZSAxMApsaWJyYXJ5KGxlYXBzKQpkYXRhLmZ1bGwgPSBkYXRhLmZyYW1lKHkgPSBZLCB4ID0gWCkKbW9kLmZ1bGwgPSByZWdzdWJzZXRzKHkgfiBwb2x5KHgsIDEwLCByYXcgPSBUKSwgZGF0YSA9IGRhdGEuZnVsbCwgbnZtYXggPSAxMCkKbW9kLnN1bW1hcnkgPSBzdW1tYXJ5KG1vZC5mdWxsKQoKIyBGaW5kIHRoZSBtb2RlbCBzaXplIGZvciBiZXN0IGNwLCBCSUMgYW5kIGFkanIyCndoaWNoLm1pbihtb2Quc3VtbWFyeSRjcCkKd2hpY2gubWluKG1vZC5zdW1tYXJ5JGJpYykKd2hpY2gubWF4KG1vZC5zdW1tYXJ5JGFkanIyKQoKIyBQbG90IGNwLCBCSUMgYW5kIGFkanIyCnBsb3QobW9kLnN1bW1hcnkkY3AsIHhsYWIgPSAiU3Vic2V0IFNpemUiLCB5bGFiID0gIkNwIiwgcGNoID0gMjAsIHR5cGUgPSAibCIpCnBvaW50cygzLCBtb2Quc3VtbWFyeSRjcFszXSwgcGNoID0gNCwgY29sID0gInJlZCIsIGx3ZCA9IDcpCnBsb3QobW9kLnN1bW1hcnkkYmljLCB4bGFiID0gIlN1YnNldCBTaXplIiwgeWxhYiA9ICJCSUMiLCBwY2ggPSAyMCwgdHlwZSA9ICJsIikKcG9pbnRzKDMsIG1vZC5zdW1tYXJ5JGJpY1szXSwgcGNoID0gNCwgY29sID0gInJlZCIsIGx3ZCA9IDcpCnBsb3QobW9kLnN1bW1hcnkkYWRqcjIsIHhsYWIgPSAiU3Vic2V0IFNpemUiLCB5bGFiID0gIkFkanVzdGVkIFIyIiwgcGNoID0gMjAsIAogICAgdHlwZSA9ICJsIikKcG9pbnRzKDMsIG1vZC5zdW1tYXJ5JGFkanIyWzNdLCBwY2ggPSA0LCBjb2wgPSAicmVkIiwgbHdkID0gNykKCiMgQ29lZnMgZm91bmQgYnkgcmVncmVzc2lvbiAocmVwbGFjZXMgeF4zIGJ5IHheNykKY29lZmZpY2llbnRzKG1vZC5mdWxsLCBpZCA9IDMpCgpgYGAKCmBgYHtyfQojIE5vdyBsYXNzbwpsaWJyYXJ5KGdsbW5ldCkKeG1hdCA9IG1vZGVsLm1hdHJpeCh5IH4gcG9seSh4LCAxMCwgcmF3ID0gVCksIGRhdGEgPSBkYXRhLmZ1bGwpWywgLTFdICMgcmVtb3ZlIGludGVyY2VwdCAoZmlyc3QgY29sdW1uKQptb2QubGFzc28gPSBjdi5nbG1uZXQoeG1hdCwgWSwgYWxwaGEgPSAxKSAjIGN2LmdsbW5ldDogY3Jvc3MgdmFsaWRhdGlvbiB0byBzZWxlY3QgYmVzdCBsYW1iZGEKYmVzdC5sYW1iZGEgPSBtb2QubGFzc28kbGFtYmRhLm1pbgpiZXN0LmxhbWJkYQpwbG90KG1vZC5sYXNzbykKCiMgTmV4dCBmaXQgdGhlIG1vZGVsIG9uIGVudGlyZSBkYXRhIHVzaW5nIGJlc3QgbGFtYmRhCmJlc3QubW9kZWwgPSBnbG1uZXQoeG1hdCwgWSwgYWxwaGEgPSAxKQpwcmVkaWN0KGJlc3QubW9kZWwsIHMgPSBiZXN0LmxhbWJkYSwgdHlwZSA9ICJjb2VmZmljaWVudHMiKQojTGFzc28gYWxzbyBwaWNrcyBYXjUgb3ZlciBYXjMuIEl0IGFsc28gcGlja3MgWF43IHdpdGggbmVnbGlnaWJsZSBjb2VmZmljaWVudC4KCmBgYAoKYGBge3J9CiMgSVNMUiAKIyBDaGFwIDcgTm9uLWxpbmVhciBNb2RlbGluZyAtIFNwbGluZXMsIEdBTQpsaWJyYXJ5KElTTFIpCmF0dGFjaChXYWdlKQpgYGAKCmBgYHtyfQpmaXQ9bG0od2FnZX5wb2x5KGFnZSw0LHJhdz1UKSxkYXRhPVdhZ2UpICAjIHJhdz1UIHRvIG9idGFpbiBjb2VmZmljaWVudHMgb2YgcG9seSBkaXJlY3RseQpjb2VmKHN1bW1hcnkoZml0KSkgIyBiZWxvdyB3ZSBzZWUgc21hbGwgY29lZiAoYW5kIHAtdmFsdWUpIGZvciBvcmRlciAzICYgNAoKIyBFcXVpdmFsZW50IGV4cHJlc3Npb25zCmZpdDJhPWxtKHdhZ2V+YWdlK0koYWdlXjIpK0koYWdlXjMpK0koYWdlXjQpLGRhdGE9V2FnZSkKY29lZihmaXQyYSkKZml0MmI9bG0od2FnZX5jYmluZChhZ2UsYWdlXjIsYWdlXjMsYWdlXjQpLGRhdGE9V2FnZSkgIyBjYmluZCBpbiBmb3JtdWxhcyA8LT4gSSgpIHdyYXBwZXIKY29lZihmaXQyYikKYGBgCgpgYGB7cn0KIyBQcmVkaWN0aW9uCmFnZWxpbXM9cmFuZ2UoYWdlKQphZ2UuZ3JpZD1zZXEoZnJvbT1hZ2VsaW1zWzFdLHRvPWFnZWxpbXNbMl0pCnByZWRzPXByZWRpY3QoZml0LG5ld2RhdGE9bGlzdChhZ2U9YWdlLmdyaWQpLHNlPVRSVUUpICMgY3JlYXRlcyBsaXN0IG9mIGFnZXMgd2hlcmUgd2Ugd2FudCBwcmVkaWN0aW9uIChzZWUgYmVsb3cpCnNlLmJhbmRzPWNiaW5kKHByZWRzJGZpdCsyKnByZWRzJHNlLmZpdCxwcmVkcyRmaXQtMipwcmVkcyRzZS5maXQpCnBhcihtZnJvdz1jKDEsMiksbWFyPWMoNC41LDQuNSwxLDEpLG9tYT1jKDAsMCw0LDApKSAjIG1hci8gb21hOiBtYXJnaW5zIG9mIHBsb3QKcGxvdChhZ2Usd2FnZSx4bGltPWFnZWxpbXMsY2V4PS41LGNvbD0iZGFya2dyZXkiKQp0aXRsZSgiRGVncmVlLTQgUG9seW5vbWlhbCIsb3V0ZXI9VCkKbGluZXMoYWdlLmdyaWQscHJlZHMkZml0LGx3ZD0yLGNvbD0iYmx1ZSIpCm1hdGxpbmVzKGFnZS5ncmlkLHNlLmJhbmRzLGx3ZD0xLGNvbD0iYmx1ZSIsbHR5PTMpCmBgYAoKYGBge3J9CmFnZS5ncmlkPXNlcShmcm9tPWFnZWxpbXNbMV0sdG89YWdlbGltc1syXSkgIyBPcjogYWdlLmdyaWQyPXNvcnQodW5pcXVlKGFnZSkpCmxpc3QoYWdlPWFnZS5ncmlkKSAjICRhZ2UgbGlzdAoKcHJlZGljdChmaXQsbGlzdChhZ2U9Yyg3MCkpKSAjIHRlc3QgZm9yIDEgYWdlIC0gc3RpbGwgbmVlZCB0byBjcmVhdGUgYW4gJGFnZSBsaXN0CmBgYAoKYGBge3J9CnByZWRzCmBgYAoKYGBge3J9CiMgVXNlIG9mIEFOT1ZBID0gQW5hbHlzaXMgb2YgdmFyaWFuY2UuIFRvIGRldGVybWluZWQgd2hpY2ggZGVncmVlIGlzIG5lZWRlZApmaXQuMT1sbSh3YWdlfmFnZSxkYXRhPVdhZ2UpCmZpdC4yPWxtKHdhZ2V+cG9seShhZ2UsMiksZGF0YT1XYWdlKQpmaXQuMz1sbSh3YWdlfnBvbHkoYWdlLDMpLGRhdGE9V2FnZSkKZml0LjQ9bG0od2FnZX5wb2x5KGFnZSw0KSxkYXRhPVdhZ2UpCmZpdC41PWxtKHdhZ2V+cG9seShhZ2UsNSksZGF0YT1XYWdlKQphbm92YShmaXQuMSxmaXQuMixmaXQuMyxmaXQuNCxmaXQuNSkgCiMgT3Igb2J0YWluIHRoZXNlIHAtdmFsdWVzIGJ5IGV4cGxvaXRpbmcgdGhlIGZhY3QgdGhhdCBwb2x5KCkgY3JlYXRlcyBvcnRob2dvbmFsIHBvbHlub21pYWxzOgpjb2VmKHN1bW1hcnkoZml0LjUpKSAjIGFuZCBjaGVjayBQLXZhbHVlcy4gRWFjaCBwLXZhbHVlIGNvcnJlc3BvbmRzIHRvIGNvbXBhcmlzb24gZnJvbSBtb2RlbCB3IHByZXZpb3VzIG9uZQpgYGAKCmBgYHtyfQojIEdlbmVyYWxpemVkIGxpbmVhciBmdW5jdGlvbgpmaXQ9Z2xtKEkod2FnZT4yNTApfnBvbHkoYWdlLDQpLGRhdGE9V2FnZSxmYW1pbHk9Ymlub21pYWwpICMgcGx1cyBjcmVhdGUgYmluYXJ5IHJlc3BvbnNlIG9uIHRoZSBmbHkgd2l0aCBJKCkKcHJlZHM9cHJlZGljdChmaXQsbmV3ZGF0YT1saXN0KGFnZT1hZ2UuZ3JpZCksc2U9VCkgIyBkZWZhdWx0OiB0eXBlPSJsaW5rIiA9IHByZWRpY3Rpb25zIGZvciBsb2dpdAojIG90aGVyIG9wdGlvbiB0eXBlPSJyZXNwb25zZSIgKHByb2JhYmlsaXR5IHZzIG9kZHMpCmBgYAoKYGBge3J9CiMgU3RlcCBmdW5jdGlvbgp0YWJsZShjdXQoYWdlLDQpKQpmaXQ9bG0od2FnZX5jdXQoYWdlLDQpLGRhdGE9V2FnZSkgIyBjdXQoKSByZXR1cm5zIGFuIG9yZGVyZWQgY2F0ZWdvcmljYWwgdmFyaWFibGUuIEJyZWFrcz0gY2FuIGJlIHVzZWQgCmNvZWYoc3VtbWFyeShmaXQpKQoKYGBgCgpgYGB7cn0KZml0LjE9bG0od2FnZX5lZHVjYXRpb24rYWdlLGRhdGE9V2FnZSkKZml0LjI9bG0od2FnZX5lZHVjYXRpb24rcG9seShhZ2UsMiksZGF0YT1XYWdlKQpmaXQuMz1sbSh3YWdlfmVkdWNhdGlvbitwb2x5KGFnZSwzKSxkYXRhPVdhZ2UpCmFub3ZhKGZpdC4xLGZpdC4yLGZpdC4zKSAjIEFub3ZhIGNhbiBjb21wYXJlIG5vdCBvcnRob2dvbmFsIHBvbHlub21pYWxzLCBhbmQgdGhhdCBoYXZlIG90aGVyIHRlcm1zCmBgYAoKYGBge3J9CiMgU3BsaW5lcwpsaWJyYXJ5KHNwbGluZXMpCmZpdD1sbSh3YWdlfmJzKGFnZSxrbm90cz1jKDI1LDQwLDYwKSksZGF0YT1XYWdlKSAjIDMga25vdHMgLS0+IDcgZGVncmVlcyBvZiBmcmVlZG9tIC0tPiAxIGludGVyY2VwdCArIDYgYmFzaXMgZnVuY3Rpb25zCnByZWQ9cHJlZGljdChmaXQsbmV3ZGF0YT1saXN0KGFnZT1hZ2UuZ3JpZCksc2U9VCkKcGxvdChhZ2Usd2FnZSxjb2w9ImdyYXkiKQpsaW5lcyhhZ2UuZ3JpZCxwcmVkJGZpdCxsd2Q9MikKbGluZXMoYWdlLmdyaWQscHJlZCRmaXQrMipwcmVkJHNlLGx0eT0iZGFzaGVkIikKbGluZXMoYWdlLmdyaWQscHJlZCRmaXQtMipwcmVkJHNlLGx0eT0iZGFzaGVkIikKYGBgCgpgYGB7cn0KcHJlZD1wcmVkaWN0KGZpdCxzZT1UKQpwbG90KGFnZSx3YWdlLGNvbD0iZ3JheSIpCmxpbmVzKGFnZSxwcmVkJGZpdCxsd2Q9MikgIyB0b28gbWFueSBwb2ludHM6IHRoYXQncyB3aHkgd2UgY2hhbmdlZCBhZ2UgaW50byBhIGxpc3Qgb2YgYWxsIHVuaXF1ZSBhZ2VzCmBgYAoKYGBge3J9CiMgVGhhdCB3b3JrcyB0b28gLSBqdXN0IG5lZWQgdG8gaGF2ZSBzYW1lIGxlbmd0aCBvZiBkYXRhIGZvciB4IGFuZCB5IGluIGxpbmVzKHgsIHkuLi4pCiMgPSBiZXd0d2VlbiBhZ2UuZ3JpZDIgYW5kIHByZWQyJGZpdAphZ2UuZ3JpZDI9c29ydCh1bmlxdWUoYWdlKSkKcHJlZDI9cHJlZGljdChmaXQsbmV3ZGF0YT1saXN0KGFnZT1hZ2UuZ3JpZDIpLHNlPVQpCnBsb3QoYWdlLHdhZ2UsY29sPSJncmF5IikKbGluZXMoYWdlLmdyaWQyLHByZWQyJGZpdCxsd2Q9MikKYGBgCgpgYGB7cn0Kc3RyKHByZWQyKQpgYGAKCmBgYHtyfQpkaW0oYnMoYWdlLGtub3RzPWMoMjUsNDAsNjApLCBkZWdyZWUgPSAzKSkgIyBicyBnZW5lcmF0ZXMgbWF0cml4IHdpdGggNiBiYXNpcyBmdW5jdGlvbnMgKHNlZSBiZWxvdykKZGltKGJzKGFnZSxkZj02KSkgIyBkZiBvcHRpb24gdG8gcHJvZHVjZSBhIHNwbGluZSB3aXRoIGtub3RzIGF0IHVuaWZvcm0gcXVhbnRpbGVzIG9mIHRoZSBkYXRhCmF0dHIoYnMoYWdlLGRmPTYpLCJrbm90cyIpCmBgYAoKYGBge3J9CiMgTmF0dXJhbCBzcGxpbmU6IG5zKCkKZml0Mj1sbSh3YWdlfm5zKGFnZSxkZj00KSxkYXRhPVdhZ2UpCnByZWQyPXByZWRpY3QoZml0MixuZXdkYXRhPWxpc3QoYWdlPWFnZS5ncmlkKSxzZT1UKSAjbGlzdCBvciBkYXRhLmZyYW1lIGZvciBwcmVkaXQoPGNsYXNzIGxtPiwgLi4pCnBsb3QoYWdlLHdhZ2UseGxpbT1hZ2VsaW1zLGNleD0uNSxjb2w9ImRhcmtncmV5IikKbGluZXMoYWdlLmdyaWQsIHByZWQyJGZpdCxjb2w9InJlZCIsbHdkPTIpCmBgYAoKYGBge3J9CiMgU21vb3RoIHNwbGluZQpwbG90KGFnZSx3YWdlLHhsaW09YWdlbGltcyxjZXg9LjUsY29sPSJkYXJrZ3JleSIpCnRpdGxlKCJTbW9vdGhpbmcgU3BsaW5lIikKZml0PXNtb290aC5zcGxpbmUoYWdlLHdhZ2UsZGY9MTYpICMgZm9yY2VzIDE2IGRlZ3JlZXMgb2YgZnJlZWRvbQpmaXQyPXNtb290aC5zcGxpbmUoYWdlLHdhZ2UsY3Y9VFJVRSkgIyBjaG9vc2UgdmFsdWUgb2YgbGFtYmRhIGJ5IGNyb3NzIHZhbGlkYXRpb24gLS0+IDYuOCBkZWdyZWVzIG9mIGZyZWVkb20KZml0MiRkZgpsaW5lcyhmaXQsY29sPSJyZWQiLGx3ZD0yKQpsaW5lcyhmaXQyLGNvbD0iYmx1ZSIsbHdkPTIpCmxlZ2VuZCgidG9wcmlnaHQiLGxlZ2VuZD1jKCIxNiBERiIsIjYuOCBERiIpLGNvbD1jKCJyZWQiLCJibHVlIiksbHR5PTEsbHdkPTIsY2V4PS44KQpgYGAKCmBgYHtyfQojIExvY2FsIHJlZ3Jlc3NzaW9uIChMb2VzcykKcGxvdChhZ2Usd2FnZSx4bGltPWFnZWxpbXMsY2V4PS41LGNvbD0iZGFya2dyZXkiKQp0aXRsZSgiTG9jYWwgUmVncmVzc2lvbiIpCmZpdD1sb2Vzcyh3YWdlfmFnZSxzcGFuPS4yLGRhdGE9V2FnZSkgIyBzcGFuPTAuMiBtZWFucyB1c2UgMjAlIG9mIG9ic2VydmF0aW9ucwpmaXQyPWxvZXNzKHdhZ2V+YWdlLHNwYW49LjUsZGF0YT1XYWdlKSAjIDUwJSBvZiBvYnNlcnZhdGlvbnMgLS0+IHNtb290aGVyCmxpbmVzKGFnZS5ncmlkLHByZWRpY3QoZml0LGRhdGEuZnJhbWUoYWdlPWFnZS5ncmlkKSksY29sPSJyZWQiLGx3ZD0yKSAjIHByZWRpY3QgbmVlZHMgZGF0YS5mcmFtZSBmb3IgbG9lc3MgZml0CmxpbmVzKGFnZS5ncmlkLHByZWRpY3QoZml0MixkYXRhLmZyYW1lKGFnZT1hZ2UuZ3JpZCkpLGNvbD0iYmx1ZSIsbHdkPTIpCmxlZ2VuZCgidG9wcmlnaHQiLGxlZ2VuZD1jKCJTcGFuPTAuMiIsIlNwYW49MC41IiksY29sPWMoInJlZCIsImJsdWUiKSxsdHk9MSxsd2Q9MixjZXg9LjgpCmBgYAoKYGBge3J9CgpgYGAKCmBgYHtyfQoKYGBgCgpgYGB7cn0KCmBgYAoKYGBge3J9CgpgYGAKCmBgYHtyfQoKYGBgCgpgYGB7cn0KCmBgYAoKYGBge3J9CgpgYGAKCmBgYHtyfQoKYGBgCgpgYGB7cn0KCmBgYAoK